Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions 11-TaskVault-Incentive-Dapp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
node_modules
.env
secret.json

# Hardhat files
/cache
/artifacts

# TypeChain files
/typechain
/typechain-types

# solidity-coverage files
/coverage
/coverage.json

# Hardhat Ignition default folder for deployments against a local node
ignition/deployments/chain-31337


172 changes: 172 additions & 0 deletions 11-TaskVault-Incentive-Dapp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# TaskVault-Incentive-Dapp
![TaskVault-Incentive-Dapp](./frontend/public/image.png)

**TaskVault-Incentive-Dapp** is a decentralized application (DApp) that enables admins to create tasks, assign rewards, and allow users to claim, complete, and get rewarded for completing tasks. Built on the Core blockchain, it integrates Web3 functionality through MetaMask to enable secure user transactions with smart contracts deployed on the Core network.

## Table of Contents
- [What is TaskVault-Incentive-Dapp?](#what-is-taskvault-incentive-dapp)
- [Learning Takeaways](#learning-takeaways)
- [Tech Stack](#tech-stack)
- [Installation](#installation)
- [Clone the Repository](#clone-the-repository)
- [Backend Setup (Smart Contracts)](#backend-setup-smart-contracts)
- [Frontend Setup](#frontend-setup)
- [Software Prerequisites](#software-prerequisites)
- [Setting up the Development Environment](#setting-up-the-development-environment)
- [Smart Contract Functions](#smart-contract-functions)
- [How to Interact with the DApp](#how-to-interact-with-the-dapp)
- [How to Deploy](#how-to-deploy)
- [Key Functionalities](#key-functionalities)
- [Git Ignore Secrets](#git-ignore-secrets)

## What is TaskVault-Incentive-Dapp?

TaskVault-Incentive-Dapp is a decentralized application that allows:
- **Admins** to create tasks with descriptions and rewards.
- **Users** to claim tasks, mark them as completed, and receive rewards once validated by an admin.

The DApp interacts with smart contracts on the Core blockchain, providing transparency and decentralization for task management, validation, and reward distribution. All transactions (task claims, completions, and validations) are recorded on the blockchain, ensuring accountability.

## Learning Takeaways

This project will help you:
- **Understand Smart Contracts**: Learn how to write and deploy smart contracts using Solidity and Hardhat.
- **Develop a Blockchain Frontend**: Create a React-based frontend that interacts with the Core blockchain via Ethers.js.
- **Integrate MetaMask**: Use MetaMask for secure interactions with smart contracts.
- **Explore Web3 Libraries**: Learn how to use Ethers.js for reading and writing to smart contracts.
- **Work with Decentralized Applications (DApps)**: Understand how DApps function by connecting frontend components with blockchain smart contracts.

## Tech Stack

- **Smart Contracts**: Solidity, OpenZeppelin, Hardhat
- **Frontend**: React.js, Ethers.js, Web3 React, RainbowKit
- **Blockchain Interaction**: MetaMask, Ethers.js

## Installation

### Clone the Repository

Start by cloning the project to your local machine:

```bash
git clone https://github.com/harystyleseze/dapp-tutorial.git
cd dapp-tutorial
```

### Backend Setup (Smart Contracts)

1. **Install dependencies**: Navigate to the backend directory and install the necessary dependencies.

```bash
cd 11-TaskVault-Incentive-Dapp
npm install
```

2. **Compile the smart contracts**:

```bash
npx hardhat compile
```

3. **Deploy the smart contracts**: Modify the `hardhat.config.js` to match your Core Testnet credentials and deploy using the following command:

```bash
npx hardhat run scripts/deploy.js
```

### Frontend Setup

1. **Install frontend dependencies**: Go to the frontend directory and install the required dependencies.

```bash
cd frontend
npm install
```

2. **Start the development server**:

```bash
npm run start
```

The app will now be accessible at [http://localhost:3000](http://localhost:3000).

## Software Prerequisites

Before you begin, ensure the following software is installed:

- [Git](https://git-scm.com/) v2.44.0
- [Node.js](https://nodejs.org/en) v20.11.1
- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) v10.2.4
- [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation) v2.22.6
- [MetaMask Extension](https://metamask.io/download/) (Chrome Extension)

### Core Testnet Configuration

Configure MetaMask to connect to the Core Testnet using the following details:
- **Network Name**: Core Testnet
- **New RPC URL**: `https://rpc.test.btcs.network`
- **Chain ID**: `1115`
- **Currency Symbol**: `CORE`

For more details, refer to the [Core Testnet Guide](https://docs.coredao.org/docs/Dev-Guide/core-testnet-wallet-config#adding-core-testnet-to-metamask).

### Core Faucet

To get test CORE tokens for transactions, visit the [Core Faucet](https://scan.test.btcs.network/faucet).

## Setting up the Development Environment

1. **Install MetaMask**: Install and configure the MetaMask extension for your browser. You can follow the installation guide [here](https://docs.coredao.org/docs/Dev-Guide/core-testnet-wallet-config).

2. **Create a `secret.json` File**: Store your sensitive private keys securely. This file will be used for deploying contracts and signing transactions. **Do not upload this file to version control**.

Example of a `secret.json` structure:

```json
{
"DeployerPrivateKey": "your_deployer_private_key",
"AssigneePrivateKey": "your_assignee_private_key"
}
```

## Smart Contract Functions

Key functions in the smart contract:

1. **createTask(description, rewardPoints)**: Allows admins to create tasks with descriptions and reward points.
2. **claimTask(taskId)**: Users can claim tasks that are not yet assigned.
3. **completeTask(taskId)**: Users mark a task as completed once they finish it.
4. **validateTask(taskId)**: Admins validate task completion and release rewards.
5. **viewTaskStatus(taskId)**: Anyone can view the task status (claimed, completed, validated).

## How to Interact with the DApp

1. **Connect Wallet**: Users must connect their MetaMask wallet to interact with the DApp.
2. **Admin Role**: Only admins can create tasks, assign them, and validate completions.
3. **UserAssignee Role**: Users can claim tasks, mark them as completed, and request validation for rewards.

## How to Deploy

1. **Modify Deployment Script**: Edit `scripts/deploy.js` with your contract details.
2. **Deploy Contracts**:

```bash
npx hardhat run scripts/deploy.js
```

## Key Functionalities

- **Task Creation**: Admins create tasks with rewards.
- **Task Claiming**: Users can claim tasks if not already claimed.
- **Task Completion**: Users mark tasks as completed after performing them.
- **Task Validation**: Admins validate task completion and issue rewards.

## Git Ignore Secrets

To prevent sensitive data from being exposed, add your `secret.json` to `.gitignore`:

```bash
# Ignore secret file containing sensitive private keys
secret.json
```
158 changes: 158 additions & 0 deletions 11-TaskVault-Incentive-Dapp/contracts/TaskRewardSystem.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract TaskRewardSystem is AccessControl, ReentrancyGuard {
enum TaskStatus { Pending, Claimed, Completed, Validated, Inactive}

struct Task {
uint id;
string description;
uint rewardPoints;
address assignee;
address validator;
TaskStatus status;
}

bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant ASSIGNEE_ROLE = keccak256("ASSIGNEE_ROLE");

address public deployer;
uint public nextTaskId;
uint public taskCount;
mapping(uint => Task) public tasks;
mapping(address => uint) public balances;

Task[] public allTasks;

event TaskCreated(uint taskId, string description, uint rewardPoints);
event TaskClaimed(uint taskId, address assignee);
event TaskCompleted(uint taskId);
event TaskValidated(uint taskId, address assignee, uint rewardPoints);
event TaskAssigned(uint taskId, address assignee);
event TaskRewardTransferred(uint taskId, address assignee, uint rewardPoints);
event AdminAssigned(address newAdmin);
event AssigneeAssigned(uint taskId, address assignee);
event TaskUpdated(uint taskId, string newDescription, uint newRewardPoints);
event TaskDeleted(uint taskId);

modifier onlyAdmin() {
require(hasRole(ADMIN_ROLE, msg.sender), "Only admin can perform this action");
_;
}

modifier onlyAssignee(uint taskId) {
require(tasks[taskId].assignee == msg.sender || hasRole(ASSIGNEE_ROLE, msg.sender), "Not authorized");
_;
}

modifier taskNotCompleted(uint taskId) {
require(tasks[taskId].status != TaskStatus.Completed, "Task is already completed");
_;
}

modifier taskNotValidated(uint taskId) {
require(tasks[taskId].status != TaskStatus.Validated, "Task is already validated");
_;
}

modifier taskCanBeClaimed(uint taskId) {
require(tasks[taskId].status != TaskStatus.Claimed, "Task already claimed");
require(tasks[taskId].status != TaskStatus.Validated, "Task already validated");
require(tasks[taskId].status != TaskStatus.Completed, "Task already completed");
require(tasks[taskId].status != TaskStatus.Inactive, "Task already deleted");
_;
}

constructor() {
deployer = msg.sender;
_grantRole(ADMIN_ROLE, msg.sender);
_setRoleAdmin(ASSIGNEE_ROLE, ADMIN_ROLE);
nextTaskId = 1;
taskCount = 0;
}

// Create task
function createTask(string memory description, uint rewardPoints) public onlyAdmin {
tasks[nextTaskId] = Task(nextTaskId, description, rewardPoints, address(0), address(0), TaskStatus.Pending);
taskCount++;
allTasks.push(tasks[nextTaskId]);
emit TaskCreated(nextTaskId, description, rewardPoints);
nextTaskId++;
}

// Assign assignee to a task (only admin can assign an assignee)
function assignAssignee(uint taskId, address assignee) public onlyAdmin {
require(tasks[taskId].assignee == address(0), "Task already has an assignee");
tasks[taskId].assignee = assignee;
_grantRole(ASSIGNEE_ROLE, assignee); // Grant ASSIGNEE_ROLE to the assignee
allTasks[taskId - 1] = tasks[taskId];
emit AssigneeAssigned(taskId, assignee);
}

// Claim task automatically
function claimTask(uint taskId) public taskNotCompleted(taskId) taskCanBeClaimed(taskId) {
tasks[taskId].assignee = msg.sender;
tasks[taskId].status = TaskStatus.Claimed;
allTasks[taskId - 1] = tasks[taskId];
emit TaskClaimed(taskId, msg.sender);
emit TaskAssigned(taskId, msg.sender); // Emit task assigned event
}

// Mark task as complete
function completeTask(uint taskId) public onlyAssignee(taskId) taskNotCompleted(taskId) {
tasks[taskId].status = TaskStatus.Completed;
allTasks[taskId - 1] = tasks[taskId];
emit TaskCompleted(taskId);
}

// Validate task and reward
function validateTask(uint taskId) public onlyAdmin taskNotValidated(taskId) nonReentrant {
require(tasks[taskId].status == TaskStatus.Completed, "Task not completed yet");
tasks[taskId].status = TaskStatus.Validated;
tasks[taskId].validator = msg.sender;
balances[tasks[taskId].assignee] += tasks[taskId].rewardPoints;
allTasks[taskId - 1] = tasks[taskId];
emit TaskValidated(taskId, tasks[taskId].assignee, tasks[taskId].rewardPoints);
emit TaskRewardTransferred(taskId, tasks[taskId].assignee, tasks[taskId].rewardPoints); // Emit reward transfer event
}

// View task status
function viewTaskStatus(uint taskId) public view returns (TaskStatus) {
return tasks[taskId].status;
}

// Assign new admin (Deployer assigns a new admin by passing their address)
function assignAdmin(address newAdmin) public {
require(msg.sender == deployer, "Only the deployer can assign a new admin");
require(newAdmin != address(0), "New admin address cannot be zero address");
_grantRole(ADMIN_ROLE, newAdmin);
emit AdminAssigned(newAdmin);
}

// Get global overview of all tasks
function getAllTasks() public view returns (Task[] memory) {
return allTasks;
}
// Get reward points for a given task
function getRewardPoints(uint taskId) public view returns (uint) {
return tasks[taskId].rewardPoints;
}

// Mark a task as inactive (instead of deleting it)
function deleteTask(uint taskId) public onlyAdmin {
tasks[taskId].status = TaskStatus.Inactive;
allTasks[taskId - 1] = tasks[taskId]; // Mark as inactive in the array
emit TaskDeleted(taskId);
}

// Update task description or reward points (only admin can use this function)
function updateTask(uint taskId, string memory newDescription, uint newRewardPoints) public onlyAdmin {
tasks[taskId].description = newDescription;
tasks[taskId].rewardPoints = newRewardPoints;
allTasks[taskId - 1] = tasks[taskId];
emit TaskUpdated(taskId, newDescription, newRewardPoints);
}
}
23 changes: 23 additions & 0 deletions 11-TaskVault-Incentive-Dapp/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
Loading