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

Rewards V2 implementation #2

Open
wants to merge 55 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
2e26fd5
Stage version of Rewards V2
dmitriy-woof-software Apr 10, 2024
1acffe7
Merge branch 'main' of github.com:woof-software/comet into woof-softw…
MishaShWoof Oct 9, 2024
401582d
feat: post audit fixes
MishaShWoof Oct 11, 2024
c3a511d
fix: types
MishaShWoof Oct 11, 2024
8291a94
fix: sort
MishaShWoof Oct 11, 2024
456f208
create logic to generate and save merkle tree for rewards V2
dmitriy-woof-software Oct 29, 2024
bc5fabf
add chunks into multicall. add generated example
dmitriy-woof-software Oct 29, 2024
fc0cab9
fix: fixes after reaudit
MishaShWoof Nov 1, 2024
c459bbd
add workflow for CometRewardsV2
vitalii-woof-software Nov 4, 2024
efe84f9
Merge branch 'compound-finance:main' into woof-software/rewards-v2-me…
vitalii-woof-software Nov 4, 2024
e4f8021
Update create-rewards-v2-campaign.yaml
vitalii-woof-software Nov 5, 2024
2bead50
Merge branch 'main' of github.com:woof-software/comet into woof-softw…
MishaShWoof Nov 6, 2024
2a10126
Merge branch 'woof-software/rewards-v2-merkle-tree-generation-script'…
MishaShWoof Nov 6, 2024
5e965e5
wip: scenarios
MishaShWoof Nov 12, 2024
a7c3bca
wip
MishaShWoof Nov 12, 2024
271bb21
wip: add new scenario
MishaShWoof Nov 14, 2024
3f8aff1
wip: cover finish root
MishaShWoof Nov 15, 2024
1b5f6ae
feat: scenarios
MishaShWoof Nov 18, 2024
0eef681
Merge branch 'main' of github.com:woof-software/comet into woof-softw…
MishaShWoof Nov 18, 2024
007f201
fix: first review
MishaShWoof Nov 20, 2024
48d7010
fix: typos
MishaShWoof Nov 21, 2024
d8127e9
feat: workflow and script for multiplier
MishaShWoof Nov 21, 2024
9b21c64
add migration for CometRewardsV2
vitalii-woof-software Nov 22, 2024
8988638
Merge branch 'woof-software/rewards-v2-multiple-rewards' of github.co…
vitalii-woof-software Nov 22, 2024
efd072e
fix: scenario and script fixes
MishaShWoof Nov 22, 2024
350ad27
Merge branch 'woof-software/rewards-v2-multiple-rewards' of https://g…
MishaShWoof Nov 22, 2024
d732422
Merge branch 'main' of github.com:woof-software/comet into woof-softw…
dmitriy-woof-software Nov 22, 2024
d1dd7f7
Merge branch 'woof-software/rewards-v2-multiple-rewards' of github.co…
dmitriy-woof-software Nov 22, 2024
1bded73
feat: migrations and multicall fix
MishaShWoof Nov 22, 2024
a8efe60
Merge branch 'woof-software/rewards-v2-multiple-rewards' of https://g…
MishaShWoof Nov 22, 2024
1001039
feat: update generation script
dmitriy-woof-software Nov 22, 2024
e1b9003
Merge branch 'woof-software/rewards-v2-multiple-rewards' of github.co…
dmitriy-woof-software Nov 22, 2024
bcec7f6
feat: add smart contract and script documentation
dmitriy-woof-software Nov 22, 2024
e826973
update script documentation
dmitriy-woof-software Nov 22, 2024
df9d19f
update script documentation
dmitriy-woof-software Nov 22, 2024
cb59418
feat: add user in campaign verification script
dmitriy-woof-software Nov 23, 2024
e42384c
feat: add retry for tree generation script
dmitriy-woof-software Nov 23, 2024
39cf898
remove comment
dmitriy-woof-software Nov 23, 2024
5358d33
run linter
dmitriy-woof-software Nov 23, 2024
b028b11
fix: update migrations
MishaShWoof Nov 24, 2024
5f26422
fix: update scenario and migrations
MishaShWoof Nov 25, 2024
79ec998
fix: clean up
MishaShWoof Nov 25, 2024
1897e53
fix
MishaShWoof Nov 26, 2024
bba83b2
feat: update docs
MishaShWoof Nov 26, 2024
c0cc701
feat: new docs
MishaShWoof Nov 27, 2024
58eb0ff
feat: add test deployment to the doc
MishaShWoof Nov 27, 2024
bc45eb8
fix: noNetwork and scenario fix
MishaShWoof Nov 28, 2024
eb740ca
fix: retry fix
MishaShWoof Nov 29, 2024
e1c4e4d
fix: scenario fixes
MishaShWoof Dec 1, 2024
6d28fdf
feat: new campaigns
MishaShWoof Dec 1, 2024
ac242a8
fix
MishaShWoof Dec 2, 2024
e641f95
fix: no network issue resolved
MishaShWoof Dec 13, 2024
0f26863
Merge branch 'woof-software/fix-to-no-network' of github.com:woof-sof…
MishaShWoof Dec 14, 2024
bf64a5a
fix: tsc
MishaShWoof Dec 17, 2024
27443c2
fix: linter
MishaShWoof Dec 17, 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
Prev Previous commit
Next Next commit
feat: update docs
MishaShWoof committed Nov 26, 2024
commit bba83b2bb7c32a81f89ba2158e0a82f1fce35c45
25 changes: 24 additions & 1 deletion docs/RewardsV2.md
Original file line number Diff line number Diff line change
@@ -46,6 +46,29 @@ New members are users who began accruing rewards after the campaign started. The
- Eliminates the need for a separate mechanism or tree for new users.
- Maintains a lightweight and scalable rewards distribution system.

### 3. **Finish Root and Snapshot Tree**

#### **What is the Finish Root?**
The `finishRoot` is a secondary Merkle tree root used to capture a **snapshot** of user reward accruals at the moment the campaign ends. This snapshot ensures accurate reward distribution, particularly for ongoing or newly joined users, by establishing a definitive final state for the campaign.

#### **Why is the Finish Root Needed?**
1. **Accurate Final Balances**:
- Rewards continue to accrue dynamically for all users during the campaign. The `finishRoot` captures the final accrued balances at the campaign’s conclusion, creating an immutable reference point for distribution.

2. **Support for New and Existing Users**:
- For **existing users**: The `finishRoot` ensures their accrued rewards are finalized and verifiable.
- For **new users**: The same as for existing users.

3. **Cost Efficiency**:
- Instead of storing final balances on-chain, the `finishRoot` compresses the data into a single root, reducing gas costs for reward claims.

---

#### **How the Finish Root is Used**
1. At the end of the campaign:
- The contract generates the `finishRoot` based on the final accrued values for all users, using the same sorted structure as the initial tree.
2. During claims:
- Users (both existing and new) provide Merkle proofs from the `finishRoot` to validate their reward balances.

---

@@ -182,4 +205,4 @@ The claim mechanism involves the following:
The new user does not generate a new proof.
Instead, they rely on existing neighbor proofs for validation.

This approach avoids the need to recompute or extend the Merkle tree. It ensures scalability and reduces gas costs for integrating new users.
This approach avoids the need to recompute or extend the Merkle tree. It ensures scalability and reduces gas costs for integrating new users.
15 changes: 14 additions & 1 deletion scripts/rewards_v2/README.md
Original file line number Diff line number Diff line change
@@ -21,4 +21,17 @@ To verify that the user was included into the Comet with the proper accrue value
## Get proofs
To get user's proof or to get user neighbors' proofs use [verify-address-in-campaign.ts](./verify-address-in-campaign.ts) script. `ADDRESS=0xUserAddress CAMPAIGN='1732326736947-21247270-start.json' DEPLOYMENT=usdt BLOCK_NUMBER=21074594 NETWORK=mainnet yarn run rewards-v2-verify-address --network mainnet`
##
To validate the list of interacted addresses with Comet please use [Dune Query](https://dune.com/queries/4320237)
To validate the list of interacted addresses with Comet please use [Dune Query](https://dune.com/queries/4320237)

# Rewards V2: Multiplayer Calculation Script

The purpose of this script is to calculate the multiplayer for the Rewards V2. Detailed information about the Rewards V2 implementation can be found in the [RewardsV2.md](../../docs/RewardsV2.md).

## Why do we need the multiplayer?

Rewards are distributed based on the borrow and supply speed of the Comet. If we need to distribute a specific amount of rewards during some time period we need to adjust the speed with the multiplier.

## How to calculate the multiplayer?

1. You can run the script locally by the command `yarn hardhat calculateMultiplier --network mainnet --deployment usdc --duration 2592000 --amount 1000`
2. Run `calculate-multiplier-for-rewards-v2-campaign` Github Workflow

Unchanged files with check annotations Beta

if(config.multiplier == 0) revert NotSupported(comet, token);
if($.finishRoot == bytes32(0) && shouldAccrue)
CometInterface(comet).accrueAccount(account);

Check warning

Code scanning / Semgrep OSS

Using nested is cheaper than using && multiple check combinations. There are more advantages, such as easier to read code and better coverage reports. Warning

Using nested is cheaper than using && multiple check combinations.
There are more advantages, such as easier to read code and better coverage reports.
uint256 claimed = $.claimed[account][token];
uint256 accrued = getRewardsAccrued(
comet,
RewardOwed[] memory owed = new RewardOwed[]($.assets.length);
if($.finishRoot == bytes32(0) && shouldAccrue)
CometInterface(comet).accrueAccount(account);

Check warning

Code scanning / Semgrep OSS

Using nested is cheaper than using && multiple check combinations. There are more advantages, such as easier to read code and better coverage reports. Warning

Using nested is cheaper than using && multiple check combinations.
There are more advantages, such as easier to read code and better coverage reports.
for (uint256 j; j < $.assets.length; j++) {
address token = $.assets[j];
) external view returns(address[] memory, AssetConfig[] memory) {
Campaign storage $ = campaigns[comet][campaignId];
AssetConfig[] memory configs = new AssetConfig[]($.assets.length);
for (uint256 i; i < $.assets.length; i++) {

Check warning

Code scanning / Semgrep OSS

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop. Warning

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.

Check warning

Code scanning / Semgrep OSS

A lot of times there is no risk that the loop counter can overflow. Using Solidity's unchecked block saves the overflow checks. Warning

A lot of times there is no risk that the loop counter can overflow.
Using Solidity's unchecked block saves the overflow checks.

Check warning

Code scanning / Semgrep OSS

Consider using the prefix increment expression whenever the return value is not needed. The prefix increment expression is cheaper in terms of gas. Warning

Consider using the prefix increment expression whenever the return value is not needed.
The prefix increment expression is cheaper in terms of gas.
configs[i] = $.configs[$.assets[i]];
}
return ($.assets, configs);
finishRoots = new bytes32[](campaigns[comet].length);
assets = new address[][](campaigns[comet].length);
finishTimestamps = new uint256[](campaigns[comet].length);
for (uint256 i; i < campaigns[comet].length; i++) {

Check warning

Code scanning / Semgrep OSS

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop. Warning

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.

Check warning

Code scanning / Semgrep OSS

Replace state variable reads and writes within loops with local variable reads and writes. Warning

Replace state variable reads and writes within loops with local variable reads and writes.

Check warning

Code scanning / Semgrep OSS

A lot of times there is no risk that the loop counter can overflow. Using Solidity's unchecked block saves the overflow checks. Warning

A lot of times there is no risk that the loop counter can overflow.
Using Solidity's unchecked block saves the overflow checks.

Check warning

Code scanning / Semgrep OSS

Consider using the prefix increment expression whenever the return value is not needed. The prefix increment expression is cheaper in terms of gas. Warning

Consider using the prefix increment expression whenever the return value is not needed.
The prefix increment expression is cheaper in terms of gas.
Campaign storage $ = campaigns[comet][i];

Check warning

Code scanning / Semgrep OSS

Replace state variable reads and writes within loops with local variable reads and writes. Warning

Replace state variable reads and writes within loops with local variable reads and writes.
startRoots[i] = $.startRoot;
finishRoots[i] = $.finishRoot;
assets[i] = $.assets;
if(amount == 0) return 0;
IERC20NonStandard(token).transfer(to, amount);
bool success;
assembly ("memory-safe") {

Check warning on line 986 in contracts/CometRewardsV2.sol

GitHub Actions / Contract linter

Avoid to use inline assembly. It is acceptable only in rare cases

Check warning on line 986 in contracts/CometRewardsV2.sol

GitHub Actions / Contract linter

Avoid to use inline assembly. It is acceptable only in rare cases
switch returndatasize()
case 0 { // This is a non-standard ERC-20
success := not(0) // set success to true
error InvalidInt256();
/// @notice Version of the price feed
uint public constant override version = 1;

Check warning

Code scanning / Semgrep OSS

A constant name is not in UPPER_CASE like other constant variables. Warning

A constant name is not in UPPER_CASE like other constant variables.
/// @notice Description of the price feed
string public description;
* @param underlyingPriceFeed_ The address of the underlying price feed to fetch prices from
* @param decimals_ The number of decimals for the returned prices
**/
constructor(address underlyingPriceFeed_, uint8 decimals_, string memory description_) {
underlyingPriceFeed = underlyingPriceFeed_;
decimals = decimals_;
description = description_;
uint8 underlyingPriceFeedDecimals = AggregatorV3Interface(underlyingPriceFeed_).decimals();
// Note: Solidity does not allow setting immutables in if/else statements
shouldUpscale = underlyingPriceFeedDecimals < decimals_ ? true : false;
rescaleFactor = (shouldUpscale
? signed256(10 ** (decimals_ - underlyingPriceFeedDecimals))
: signed256(10 ** (underlyingPriceFeedDecimals - decimals_))
);
}

Check warning

Code scanning / Semgrep OSS

There're no sanity checks for the constructor argument decimals_. Warning

There're no sanity checks for the constructor argument decimals_.

Check warning

Code scanning / Semgrep OSS

There're no sanity checks for the constructor argument description_. Warning

There're no sanity checks for the constructor argument description_.

Check warning

Code scanning / Semgrep OSS

There're no sanity checks for the constructor argument underlyingPriceFeed_. Warning

There're no sanity checks for the constructor argument underlyingPriceFeed_.

Check warning

Code scanning / Semgrep OSS

Consider making costructor payable to save gas. Warning

Consider making costructor payable to save gas.
/**
* @notice Price for the latest round
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {

Check warning on line 226 in contracts/MerkleProof.sol

GitHub Actions / Contract linter

Avoid to use inline assembly. It is acceptable only in rare cases
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)