Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/x2earn rewards pool #19

Merged
merged 23 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
070f2b3
feat: set x2EarnRewardsPool contract and appId in EcoEarn
Agilulfo1820 Jun 22, 2024
c973386
feat: distribute rewards through dao contract
Agilulfo1820 Jun 22, 2024
5f5ab07
feat: use x2EarnContract to distribute token
Agilulfo1820 Jun 22, 2024
e2cd036
revert: claimAllocation
Agilulfo1820 Jun 22, 2024
441c40c
feat: admin does not need to claim, but only set amount for next cycle
Agilulfo1820 Jun 22, 2024
619ffa1
feat: remove IToken from EcoEarn contract (not needed anymore)
Agilulfo1820 Jun 22, 2024
37c4a32
Merge branch 'main' into feat/x2earn-rewards-pool
Agilulfo1820 Jun 22, 2024
3fe609b
Merge remote-tracking branch 'origin/main' into feat/x2earn-rewards-pool
Agilulfo1820 Jul 15, 2024
8ada0f5
fix: compile contract
Agilulfo1820 Jul 17, 2024
2f26f3e
feat: added mock contracts
Agilulfo1820 Jul 23, 2024
ea3002c
feat: added new deploy script
Agilulfo1820 Jul 23, 2024
5b8b140
feat: updated test deploy helper
Agilulfo1820 Jul 23, 2024
e3e82b8
chore: add docs in readme
Agilulfo1820 Jul 23, 2024
5ce629a
fix: tests
Agilulfo1820 Jul 23, 2024
7e1fec3
fix: yarn lint
Agilulfo1820 Jul 23, 2024
af3f055
fix: warn devs to use the test sandbox
Agilulfo1820 Jul 23, 2024
fd9e7ad
chore: added comment regarding sustainability proof
Agilulfo1820 Jul 23, 2024
0bfad30
feat: updated readme
Agilulfo1820 Jul 23, 2024
aec30a8
chore: added testnet env link
Agilulfo1820 Jul 23, 2024
cfaae32
chore: updated readme
Agilulfo1820 Jul 23, 2024
5b084b2
Merge remote-tracking branch 'origin/main' into feat/x2earn-rewards-pool
Agilulfo1820 Jul 23, 2024
75e857c
fix: typo
Agilulfo1820 Jul 23, 2024
75fa6ed
feat: added more steps in readme
Agilulfo1820 Jul 23, 2024
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
103 changes: 91 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@

Unlock the potential of decentralized application development on Vechain with our X-App template for VeBetterDAO. Designed for the Vechain Thor blockchain, this template integrates cutting-edge technologies such as React, TypeScript, Hardhat, and Express, ensuring a seamless and efficient DApp development experience. 🌟

Read more about the implementation and key features of this template in our [Developer Docs](https://docs.vebetterdao.org/developer-guides/integration-examples/pattern-2-use-smart-contracts-and-backend).

This template uses the VeBetterDAO ecosystem to distribute rewards to users. To learn more about VeBetterDAO, visit our [documentation](https://docs.vebetterdao.org/developer-guides/integration-examples).

When using the solo node you can import the following mnemonic into your wallet and have access to 10 pre-funded accounts:

```
denial kitchen pet squirrel other broom bar gas better priority spoil cross
```

## Requirements

Ensure your development environment is set up with the following:
Expand All @@ -33,14 +43,17 @@ Ensure your development environment is set up with the following:
- **Hardhat (for smart contracts):** [Getting Started with Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started) ⛑️

## Project Structure

### Frontend (apps/frontend) 🌐

A blazing-fast React application powered by Vite:

- **Vechain dapp-kit:** Streamline wallet connections and interactions. [Learn more](https://docs.vechain.org/developer-resources/sdks-and-providers/dapp-kit)

### Backend (apps/backend) 🔙

An Express server crafted with TypeScript for robust API development:

- **Vechain SDK:** Seamlessly fetch and perform transactions with the VechainThor blockchain. [Learn more](https://docs.vechain.org/developer-resources/sdks-and-providers/sdk)
- **OpenAI GPT-Vision-Preview:** Integrate image analysis capabilities. [Explore here](https://platform.openai.com/docs/guides/vision)

Expand All @@ -59,29 +72,41 @@ Configure your environment variables for seamless integration:
### Frontend

Place your `.env` files in `apps/frontend`:

- **VITE_RECAPTCHA_V3_SITE_KEY:** [Request your RecaptchaV3 site keys](https://developers.google.com/recaptcha/docs/v3)

### Backend

Store your environment-specific `.env` files in `apps/backend`. `.env.development.local` & `.env.production.local` allow for custom environment variables based on the environment:

- **OPENAI_API_KEY:** [Get your GPT-4 OpenAI key](https://platform.openai.com/api-keys) (Enable GPT-4 [here](https://help.openai.com/en/articles/7102672-how-can-i-access-gpt-4-gpt-4-turbo-and-gpt-4o))
- **RECAPTCHA_SECRET_KEY:** [Request your RecaptchaV3 site keys](https://developers.google.com/recaptcha/docs/v3)

### Contracts

Manage deployment parameters and network configurations in `hardhat.config.js` under `apps/contracts`:

- **MNEMONIC:** Mnemonic of the deploying wallet

## Getting Started 🏁

Clone the repository and install dependencies with ease:

```bash
yarn install # Run this at the root level of the project
```

To distribute rewards this contract necesitates of a valid APP_ID provided by VeBetterDAO when joining the ecosystem.
In testnet you can generate the APP_ID by using the [VeBetterDAO sandbox](https://dev.testnet.governance.vebetterdao.org/).
This contract can be initially deployed without this information and DEFAULT_ADMIN_ROLE can update it later through {EcoEarn-setAppId}.

This contract must me set as a `rewardDistributor` inside the X2EarnApps contract to be able to send rewards to users and withdraw.

## Deploying Contracts 🚀

Deploy your contracts effortlessly on either the Testnet or Solo networks:
Deploy your contracts effortlessly on either the Testnet or Solo networks.
If you are deploying on the Testnet, ensure you have the correct addresses in the `config-contracts` package (generated on the [VeBetterDAO sandbox](https://dev.testnet.governance.vebetterdao.org/)).
When deploying on the SOLO network the script will deploy for you the mocked VeBetterDAO contracts and generate an APP_ID.

### Deploying on Solo Network

Expand All @@ -98,25 +123,79 @@ yarn contracts:deploy:testnet
## Running on Solo Network 🔧

Run the Frontend and Backend, connected to the Solo network and pointing to your deployed contracts. Ensure all environment variables are properly configured:

```bash
yarn dev
yarn dev
```

### Setting up rewards
Run vechain devpal

## Testnet

Read the [VeBetterDAO documentation](https://docs.vebetterdao.org/developer-guides/test-environmnet) to learn how to set up rewards for your users and use the Testnet environment.

Test environment: [https://dev.testnet.governance.vebetterdao.org/](https://dev.testnet.governance.vebetterdao.org/)

<div style="width: 100%; height: 0px; position: relative; padding-bottom: 57.971%;"><iframe src="https://streamable.com/e/175r1s?quality=highest" frameborder="0" width="100%" height="100%" allowfullscreen style="width: 100%; height: 100%; position: absolute;"></iframe></div>

Thanks to the test environment you will be able to mint and deposit B3TR tokens int the rewards pool that you will use to distribute rewards to users.

Now you just need to trigger cycles and set amount of rewards per cycle on your EcoEarn contract.

1. Go to our online [inspector app](https://solid-funicular-1wmop55.pages.github.io/#/contracts) that you can use to interact with your contracts. Be sure to select the correct network (Testnet).

2. Add the `EcoEarn` contract to the inspector app. Get the address from `config-contracts` package and the ABI from the `apps/contracts/artifacts/contracts/EcoEarn.sol/EcoEarn.json` file.
![image](https://i.ibb.co/TK8519c/SCR-20240723-kjid.png)

3. Set how many rewards you want to distribute per cycle:
![image](https://i.ibb.co/qpJnL5x/SCR-20240723-kkti.png)

4. Trigger a cycle:
![image](https://i.ibb.co/47V2Zjb/SCR-20240723-kkxx.png)

## Solo Network

Since the Solo network is a local network with mocked VeBetterDAO contracts you can use the following steps to set up available rewards to distribute to users:

0. Ensure you are using a wallet with imported pre-funded accounts mnemonic into your wallet. Mnemoninc:

```
denial kitchen pet squirrel other broom bar gas better priority spoil cross
```

1. Copy the `APP_ID` generated by the `contracts:deploy:solo` script and logged in the console.
2. Run `devpal`, a frontend tool to interact with your contracts:

```bash
npx @vechain/devpal http://localhost:8669
```

Open the `Inspector` tab and perform the following transactions:
- **Add Contracts:** Add the EcoEarn contract and the Token contract deployed previously. Addresses can be found in the `config-contracts` package. ABIs can be found in the artifacts folder of the `contracts` app.
![image](https://github.com/vechain/x-app-template/assets/64158778/e288ada4-5973-4428-9e72-a362388b1826)
- **Approve token:** Approve the EcoEarn contract to spend your tokens
![image](https://github.com/vechain/x-app-template/assets/64158778/70787d8d-ae60-40ea-b277-87359aaca4ee)
- **Claim rewards:** Claim rewards for the EcoEarn contract
![image](https://github.com/vechain/x-app-template/assets/64158778/834437e5-8de1-4802-9ed7-dca6fe4df332)
- **Trigger cycle:** Trigger the cycle for the EcoEarn contract
![image](https://github.com/vechain/x-app-template/assets/64158778/00236dcd-5b64-4493-9acd-55c6a7f0981f)
3. Open the `Inspector` tab and perform the following actions:
4. Add the B3TR_Mock contract (get the address from the console logs and ABI from the `apps/contracts/artifacts/contracts/mock/B3TR_Mock.sol/B3TR_Mock.json` file)
![image](https://i.ibb.co/6Zrj7Nx/SCR-20240723-jorq.png)
5. Add the X2EarnRewardsPool contract (get the address from the console logs and ABI from the `apps/contracts/artifacts/contracts/mock/X2EarnRewardsPoolMock.sol/X2EarnRewardsPoolMock.json` file)
![image](https://i.ibb.co/yYjLw9v/SCR-20240723-jozk.png)
6. You should now have the following setup:
![image](https://i.ibb.co/w4XWyh9/SCR-20240723-jpbc.png)
7. To recharge the rewards pool you will need to mint some mocked B3TR tokens, then deposit them into the rewards pool. Perform the following actions:
- Mint some tokens by calling the `mint` function on the B3TR_Mock contract
![image](https://i.ibb.co/XCQ7LNR/SCR-20240723-kgll.png)
- Approve the X2EarnRewards contract to spend the tokens by calling the `approve` function on the B3TR_Mock contract
![image](https://i.ibb.co/X7Txx7Y/SCR-20240723-keuu.png)
- Deposit the tokens into the rewards pool by calling the `deposit` function on the X2EarnRewardsPool contract
![image](https://i.ibb.co/X7Txx7Y/SCR-20240723-keuu.png)
8. Now you just need to set how many rewards you want to distribute per cycle and trigger the start of the cycle

- Add the `EcoEarn` contract to the inspector app. Get the address from `config-contracts` package and the ABI from the `apps/contracts/artifacts/contracts/EcoEarn.sol/EcoEarn.json` file.
![image](https://i.ibb.co/TK8519c/SCR-20240723-kjid.png)

- Set how many rewards you want to distribute per cycle:
![image](https://i.ibb.co/qpJnL5x/SCR-20240723-kkti.png)

- Trigger a cycle:
![image](https://i.ibb.co/47V2Zjb/SCR-20240723-kkxx.png)

NB: Values are in wei, use this tool to convert to VET: [https://eth-converter.com/](https://eth-converter.com/)

## Disclaimer ⚠️

Expand Down
61 changes: 41 additions & 20 deletions apps/contracts/contracts/EcoEarn.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,26 @@
pragma solidity ^0.8.19;

import '@openzeppelin/contracts/access/AccessControl.sol';
import './interfaces/IToken.sol';
import './interfaces/IX2EarnRewardsPool.sol';
import '@openzeppelin/contracts/utils/Strings.sol';

/**
* @title EcoEarn Contract
* @dev This contract manages a reward system based on cycles. Participants can make valid submissions to earn rewards.
* Rewards are being distributed by interacting with the VeBetterDAO's X2EarnRewardsPool contract.
*
* @notice To distribute rewards this contract necesitates of a valid APP_ID provided by VeBetterDAO when joining the ecosystem.
* In testnet you can generate the APP_ID by using the VeBetterDAO sandbox at https://dev.testnet.governance.vebetterdao.org/.
* This contract can be initially deployed without this information and DEFAULT_ADMIN_ROLE can update it later through {EcoEarn-setAppId}.
*
* @notice This contract must me set as a `rewardDistributor` inside the X2EarnApps contract to be able to send rewards to users and withdraw.
*/
contract EcoEarn is AccessControl {
IToken public token;
// The X2EarnRewardsPool contract used to distribute rewards
IX2EarnRewardsPool public x2EarnRewardsPoolContract;

// AppID given by the X2EarnApps contract of VeBetterDAO
bytes32 public appId;

// Mapping from cycle to total rewards
mapping(uint256 => uint256) public rewards;
Expand Down Expand Up @@ -67,15 +79,20 @@ contract EcoEarn is AccessControl {
/**
* @dev Constructor for the EcoEarn contract
* @param _admin Address of the admin
* @param _token Address of the token contract
* @param _x2EarnRewardsPoolContract Address of the X2EarnRewardsPool contract
* @param _cycleDuration Duration of each cycle in blocks
* @param _maxSubmissionsPerCycle Maximum submissions allowed per cycle
* @param _appId The appId generated by the X2EarnApps contract when app was added to VeBetterDAO
*/
constructor(address _admin, address _token, uint256 _cycleDuration, uint256 _maxSubmissionsPerCycle) {
token = IToken(_token);
constructor(address _admin, address _x2EarnRewardsPoolContract, uint256 _cycleDuration, uint256 _maxSubmissionsPerCycle, bytes32 _appId) {
require(_admin != address(0), 'EcoEarn: _admin address cannot be the zero address');
require(_x2EarnRewardsPoolContract != address(0), 'EcoEearn: x2EarnRewardsPool contract address cannot be the zero address');

x2EarnRewardsPoolContract = IX2EarnRewardsPool(_x2EarnRewardsPoolContract);
maxSubmissionsPerCycle = _maxSubmissionsPerCycle;
cycleDuration = _cycleDuration;
nextCycle = 1;
appId = _appId;
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
}

Expand Down Expand Up @@ -106,21 +123,23 @@ contract EcoEarn is AccessControl {
// Decrease the rewards left
rewardsLeft[getCurrentCycle()] -= amount;

// Transfer the reward to the participant
require(token.transfer(participant, amount));
// Transfer the reward to the participant, will revert if the transfer fails
// The last parameter is the proof of the sustainable action the user is rewarded for.
// It is optional and can be left empty, but it is recommended to provide a proof for transparency and to avoid disputes.
// Read more about proofs in the VeBetterDAO documentation: https://docs.vebetterdao.org/developer-guides/sustainability-proofs
x2EarnRewardsPoolContract.distributeReward(appId, amount, participant, '');
Agilulfo1820 marked this conversation as resolved.
Show resolved Hide resolved

emit Submission(participant, amount);
}

/**
* @dev Claims allocation for the next cycle
* @dev Set the allocation for the next cycle
* @param amount Amount of tokens to be allocated
*/
function claimAllocation(uint256 amount) public onlyRole(DEFAULT_ADMIN_ROLE) {
require(amount <= token.balanceOf(msg.sender), 'EcoEarn: Insufficient balance');
function setRewardsAmount(uint256 amount) public onlyRole(DEFAULT_ADMIN_ROLE) {
require(amount <= x2EarnRewardsPoolContract.availableFunds(appId), 'EcoEarn: Insufficient balance on the X2EarnRewardsPool contract');
rewards[nextCycle] = amount;
rewardsLeft[nextCycle] = amount;
require(token.transferFrom(msg.sender, address(this), amount));
emit ClaimedAllocation(nextCycle, amount);
}

Expand All @@ -133,7 +152,9 @@ contract EcoEarn is AccessControl {
require(cycle < getCurrentCycle(), 'EcoEarn: Cycle is not over');
uint256 amount = rewardsLeft[cycle];
rewardsLeft[cycle] = 0;
require(token.transfer(msg.sender, amount));

// will revert if the withdraw fails
x2EarnRewardsPoolContract.withdraw(amount, appId, string.concat('Withdraws remaining rewards of cycle nr.', Strings.toString(cycle)));
}

// ---------------- SETTERS ---------------- //
Expand All @@ -147,14 +168,6 @@ contract EcoEarn is AccessControl {
maxSubmissionsPerCycle = _maxSubmissionsPerCycle;
}

/**
* @dev Sets the token address
* @param _token New token contract address
*/
function setToken(address _token) external onlyRole(DEFAULT_ADMIN_ROLE) {
token = IToken(_token);
}

/**
* @dev Sets the next cycle number
* @param _nextCycle New next cycle number
Expand All @@ -163,6 +176,14 @@ contract EcoEarn is AccessControl {
nextCycle = _nextCycle;
}

/**
* @dev Sets the appId provied by VeBetterDAO
* @param _appId The new app id
*/
function setAppId(bytes32 _appId) external onlyRole(DEFAULT_ADMIN_ROLE) {
appId = _appId;
}

// ---------------- GETTERS ---------------- //

/**
Expand Down
14 changes: 0 additions & 14 deletions apps/contracts/contracts/Token.sol

This file was deleted.

83 changes: 83 additions & 0 deletions apps/contracts/contracts/interfaces/IX2EarnRewardsPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
* @title IX2EarnRewardsPool
* @dev Interface designed to be used by a contract that allows x2Earn apps to reward users that performed sustainable actions.
* Funds can be deposited into this contract by specifying the app id that can access the funds.
* Admins of x2EarnApps can withdraw funds from the rewards pool, whihc are sent to the team wallet.
*/
interface IX2EarnRewardsPool {
/**
* @dev Event emitted when a new deposit is made into the rewards pool.
*
* @param amount The amount of $B3TR deposited.
* @param appId The ID of the app for which the deposit was made.
* @param depositor The address of the user that deposited the funds.
*/
event NewDeposit(uint256 amount, bytes32 indexed appId, address indexed depositor);

/**
* @dev Event emitted when a team withdraws funds from the rewards pool.
*
* @param amount The amount of $B3TR withdrawn.
* @param appId The ID of the app for which the withdrawal was made.
* @param teamWallet The address of the team wallet that received the funds.
* @param withdrawer The address of the user that withdrew the funds.
* @param reason The reason for the withdrawal.
*/
event TeamWithdrawal(uint256 amount, bytes32 indexed appId, address indexed teamWallet, address withdrawer, string reason);

/**
* @dev Event emitted when a reward is emitted by an app.
*
* @param amount The amount of $B3TR rewarded.
* @param appId The ID of the app that emitted the reward.
* @param receiver The address of the user that received the reward.
* @param proof The proof of the sustainable action that was performed.
* @param distributor The address of the user that distributed the reward.
*/
event RewardDistributed(uint256 amount, bytes32 indexed appId, address indexed receiver, string proof, address indexed distributor);

/**
* @dev Retrieves the current version of the contract.
*
* @return The version of the contract.
*/
function version() external pure returns (string memory);

/**
* @dev Function used by x2earn apps to deposit funds into the rewards pool.
*
* @param amount The amount of $B3TR to deposit.
* @param appId The ID of the app.
*/
function deposit(uint256 amount, bytes32 appId) external returns (bool);

/**
* @dev Function used by x2earn apps to withdraw funds from the rewards pool.
*
* @param amount The amount of $B3TR to withdraw.
* @param appId The ID of the app.
* @param reason The reason for the withdrawal.
*/
function withdraw(uint256 amount, bytes32 appId, string memory reason) external;

/**
* @dev Gets the amount of funds available for an app to reward users.
*
* @param appId The ID of the app.
*/
function availableFunds(bytes32 appId) external view returns (uint256);

/**
* @dev Function used by x2earn apps to reward users that performed sustainable actions.
*
* @param appId the app id that is emitting the reward
* @param amount the amount of B3TR token the user is rewarded with
* @param receiver the address of the user that performed the sustainable action and is rewarded
* @param proof a JSON file uploaded on IPFS by the app that adds information on the type of action that was performed
*/
function distributeReward(bytes32 appId, uint256 amount, address receiver, string memory proof) external;
}
Loading
Loading