diff --git a/foundry.toml b/foundry.toml index a7c4397..3c328cf 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,7 @@ auto_detect_solc = false src = 'contracts' out = 'out' test = 'test' -libs = ['node_modules'] +libs = ["node_modules", "lib"] script = 'scripts' cache_path = 'cache-forge' gas_reports = ["*"] @@ -20,7 +20,7 @@ runs = 500 runs = 500 [profile.dev] -via_ir = false +via_ir = true [rpc_endpoints] localhost = "${LOCALHOST_NODE_URI}" @@ -63,6 +63,8 @@ lisk = "${LISK_NODE_URI}" etherlink = "${ETHERLINK_NODE_URI}" arthera = "${ARTHERA_NODE_URI}" swell = "${SWELL_NODE_URI}" +fork = "${ETH_NODE_URI_FORK}" + [etherscan] localhost = { url = "http://localhost:4000", key = "${LOCALHOST_ETHERSCAN_API_KEY}" } mainnet = { chainId = 1, key = "${MAINNET_ETHERSCAN_API_KEY}", url = "https://api.etherscan.io/api" } @@ -103,4 +105,4 @@ worldchain = { chainId = 480, key = "${WORLDCHAIN_ETHERSCAN_API_KEY}", url = "ht lisk = { chainId = 1135, key = "${LISK_ETHERSCAN_API_KEY}", url = "https://blockscout.lisk.com/api/" } etherlink = { chainId = 42793, key = "${ETHERLINK_ETHERSCAN_API_KEY}", url= "https://explorer.etherlink.com/api" } arthera = { chainId = 10242, key = "${ARTHERA_ETHERSCAN_API_KEY}", url = "https://explorer.arthera.net/api/" } -swell = { chainId = 1923, key = "${SWELL_ETHERSCAN_API_KEY}", url = "https://explorer.swellnetwork.io:443/api/" } \ No newline at end of file +swell = { chainId = 1923, key = "${SWELL_ETHERSCAN_API_KEY}", url = "https://explorer.swellnetwork.io:443/api/" } diff --git a/lib/utils b/lib/utils index d4a8da3..53dafaf 160000 --- a/lib/utils +++ b/lib/utils @@ -1 +1 @@ -Subproject commit d4a8da343cd8de28885ec5a2352d3be1cbd3b0ec +Subproject commit 53dafafba5ca39c7e519b919f90d04dd316f643f diff --git a/package.json b/package.json index eb410df..3c47200 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "ci:coverage": "forge coverage --ir-minimum --report lcov && yarn lcov:clean", "lcov:clean": "lcov --remove lcov.info -o lcov.info 'test/**' 'scripts/**' 'contracts/mock/**' 'contracts/deprecated/**' 'contracts/external/**'", "lcov:generate-html": "genhtml lcov.info --output=coverage", + "fork": "bash helpers/fork.sh", "foundry:compile": "forge build --optimize --optimizer-runs 1000", "foundry:coverage": "forge coverage --ir-minimum --report lcov && yarn lcov:clean && yarn lcov:generate-html", "foundry:script": "forge script -vvvv", @@ -15,6 +16,10 @@ "foundry:setup": "curl -L https://foundry.paradigm.xyz | bash && foundryup && git submodule update --init --recursive", "foundry:size": "forge build --skip test --sizes", "foundry:test": "FOUNDRY_PROFILE=dev forge test -vvv", + "impersonate": "cast rpc anvil_impersonateAccount", + "impersonate:script": "FOUNDRY_PROFILE=dev forge script --skip test --fork-url fork --broadcast -vvvv --gas-price 0 --priority-gas-price 0 --unlocked --sender", + "impersonate:setBalance": "cast rpc anvil_setBalance 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776 1000000000000000000 && cast rpc anvil_setBalance 0x15775b23340C0f50E0428D674478B0e9D3D0a759 1000000000000000000 && cast rpc anvil_setBalance 0x19c41f6607b2c0e80e84baadaf886b17565f278e 1000000000000000000 && cast rpc anvil_setBalance 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701 1000000000000000000", + "fork:advanceTime": "cast rpc evm_increaseTime 704800 && cast rpc anvil_mine", "lint": "solhint --max-warnings 20 \"contracts/**/*.sol\"", "prettier": "prettier --ignore-path .gitignore --write '*.{js,ts,sol}'" }, @@ -36,4 +41,4 @@ "forge-std": "github:foundry-rs/forge-std#v1.9.4" }, "dependencies": {} -} +} \ No newline at end of file diff --git a/remappings.txt b/remappings.txt index d951543..79003cf 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,2 +1,4 @@ @openzeppelin/=node_modules/@openzeppelin/ -forge-std/=node_modules/forge-std/src \ No newline at end of file +forge-std/=node_modules/forge-std/src +oz/=node_modules/@openzeppelin/contracts/ +utils/=lib/utils \ No newline at end of file diff --git a/scripts/foundry/ClaimDistributor.s.sol b/scripts/foundry/ClaimDistributor.s.sol new file mode 100644 index 0000000..5a1c45a --- /dev/null +++ b/scripts/foundry/ClaimDistributor.s.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; +import { Distributor } from "contracts/Distributor.sol"; +import { MockToken, IERC20 } from "contracts/mock/MockToken.sol"; +import { CommonUtils, ContractType } from "utils/src/CommonUtils.sol"; +import { CHAIN_BASE } from "utils/src/Constants.sol"; +import { StdAssertions } from "forge-std/Test.sol"; + +contract ClaimDistributor is CommonUtils, StdAssertions { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + /// TODO: COMPLETE + uint256 chainId = CHAIN_BASE; + IERC20 rewardToken = IERC20(0xC011882d0f7672D8942e7fE2248C174eeD640c8f); + address claimer = 0x15775b23340C0f50E0428D674478B0e9D3D0a759; + uint256 balanceToClaim = 1918683165360; + bytes32[][] memory proofs = new bytes32[][](1); + address[] memory users = new address[](1); + address[] memory tokens = new address[](1); + uint256[] memory amounts = new uint256[](1); + proofs[0] = new bytes32[](17); + proofs[0][0] = bytes32(0xb4273243bd0ec5add5e6d803f13bf6866ed1904d24626766ab2836454ba1ec0a); + proofs[0][1] = bytes32(0x3ee0ead23e2fe3f664ccb5e13683f27e27a4d7fefa8405545fb6421244630375); + proofs[0][2] = bytes32(0x69f54e33351af15236b33bb4695470f1af96cd1a9f154aa511ff16faa6886791); + proofs[0][3] = bytes32(0xa9d77ad46850fbfb8c196c693acdbb0c6241a2e561a8b0073ec71297a565673d); + proofs[0][4] = bytes32(0xe1b57f280e556c7f217e8d375f0cef7977a9467d5496d32bb8ec461f0d4c4f19); + proofs[0][5] = bytes32(0x0fc7ddc7cc9ecc7f7b0be5692f671394f6245ffdabe5c0fd2062eb71b7c11826); + proofs[0][6] = bytes32(0x94445a98fe6679760e5ac2edeacfe0bfa397f805c7adeaf3558a82accb78f201); + proofs[0][7] = bytes32(0x14a6fec66cdfece5c73ec44196f1414326236131ff9a60350cca603e54985c4e); + proofs[0][8] = bytes32(0x84679751230af3e3242ea1cecfc8daee3d2187ab647281cbf8c52e649a43e84c); + proofs[0][9] = bytes32(0xc0fc15960178fe4d542c93e64ec58648e5ff17bd02b27f841bd6ab838fc5ee67); + proofs[0][10] = bytes32(0x9b84efe5d11bc4de32ecd204c3962dd9270694d93a50e2840d763eaeac6c194b); + proofs[0][11] = bytes32(0x5c8025dbe663cf4b4e19fbc7b1e54259af5822fd774fd60a98e7c7a60112efe0); + proofs[0][12] = bytes32(0x301b573f9a6503ebe00ff7031a33cd41170d8b4c09a31fcafb9feb7529400a79); + proofs[0][13] = bytes32(0xc89942ad2dcb0ac96d2620ef9475945bdbe6d40a9f6c4e9f6d9437a953bf881c); + proofs[0][14] = bytes32(0xce6ca90077dc547f9a52a24d2636d659642fbae1d16c81c9e47c5747a472c63f); + proofs[0][15] = bytes32(0xe34667d2e10b515dd1f7b29dcd7990d25ea9caa7a7de571c4fb221c0a8fc82a1); + proofs[0][16] = bytes32(0x8316d6488fd22b823cc35ee673297ea2a753f0a89e5384ef20b38d053c881628); + users[0] = claimer; + tokens[0] = address(rewardToken); + amounts[0] = balanceToClaim; + /// END + + Distributor distributor = Distributor(_chainToContract(chainId, ContractType.Distributor)); + + vm.startBroadcast(claimer); + + distributor.claim(users, tokens, amounts, proofs); + + assertEq(rewardToken.balanceOf(claimer), balanceToClaim); + + vm.stopBroadcast(); + } +} diff --git a/scripts/foundry/CreateCampaign.s.sol b/scripts/foundry/CreateCampaign.s.sol new file mode 100644 index 0000000..7d90342 --- /dev/null +++ b/scripts/foundry/CreateCampaign.s.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; +import { DistributionCreator, DistributionParameters, CampaignParameters } from "contracts/DistributionCreator.sol"; +import { MockToken, ERC20 } from "contracts/mock/MockToken.sol"; +import { CommonUtils, ContractType } from "utils/src/CommonUtils.sol"; +import { Constants } from "utils/src/Constants.sol"; +import { StdAssertions } from "forge-std/Test.sol"; + +contract CreateTestCampaign is CommonUtils, StdAssertions { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + /// TODO: COMPLETE + uint256 chainId = Constants.CHAIN_BASE; + ERC20 rewardToken = ERC20(0xC011882d0f7672D8942e7fE2248C174eeD640c8f); + uint256 amount = 1000 ether; + /// END + + DistributionCreator distributionCreator = DistributionCreator( + _chainToContract(chainId, ContractType.DistributionCreator) + ); + + vm.startBroadcast(deployer); + + MockToken(address(rewardToken)).mint(deployer, amount); + rewardToken.approve(address(distributionCreator), amount); + uint32 startTimestamp = uint32(1718253319); + bytes32 campaignId = distributionCreator.createCampaign( + CampaignParameters({ + campaignId: bytes32(0), + creator: deployer, + rewardToken: address(rewardToken), + amount: amount, + campaignType: 1, + startTimestamp: startTimestamp, + duration: 3600 * 24 * 10, + campaignData: abi.encode( + 0xbEEfa1aBfEbE621DF50ceaEF9f54FdB73648c92C, + new address[](0), + new address[](0), + "", + new bytes[](0), + new bytes[](0), + hex"" + ) + }) + ); + + CampaignParameters memory campaign = distributionCreator.campaign(campaignId); + assertEq(campaign.creator, deployer); + assertEq(campaign.rewardToken, address(rewardToken)); + assertEq(campaign.amount, (amount * (1e9 - distributionCreator.defaultFees())) / 1e9); + assertEq(campaign.campaignType, 1); + assertEq(campaign.startTimestamp, startTimestamp); + assertEq(campaign.duration, 3600 * 24 * 10); + + vm.stopBroadcast(); + } +} diff --git a/scripts/foundry/ReallocateCampaignRewards.s.sol b/scripts/foundry/ReallocateCampaignRewards.s.sol new file mode 100644 index 0000000..1c8f10f --- /dev/null +++ b/scripts/foundry/ReallocateCampaignRewards.s.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; +import { DistributionCreator, DistributionParameters, CampaignParameters } from "contracts/DistributionCreator.sol"; +import { MockToken, IERC20 } from "contracts/mock/MockToken.sol"; +import { CommonUtils, ContractType } from "utils/src/CommonUtils.sol"; +import { CHAIN_BASE } from "utils/src/Constants.sol"; +import { StdAssertions } from "forge-std/Test.sol"; +import { ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; + +contract ReallocateCampaignRewards is CommonUtils, StdAssertions { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + /// TODO: COMPLETE + uint256 chainId = CHAIN_BASE; + IERC20 rewardToken = IERC20(0xC011882d0f7672D8942e7fE2248C174eeD640c8f); + uint256 amount = 97 ether; // after fees + bytes32 campaignId = 0x1d1231a7a6958431a5760b929c56f0e44a20f06e92a52324c19a2e4d2ec529bc; + address to = 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701; + address[] memory froms = new address[](2); + froms[0] = 0x15775b23340C0f50E0428D674478B0e9D3D0a759; + froms[1] = 0xe4BB74804edf5280c9203f034036f7CB15196078; + /// END + + DistributionCreator distributionCreator = DistributionCreator( + _chainToContract(chainId, ContractType.DistributionCreator) + ); + uint32 timestamp = uint32(block.timestamp); + + vm.startBroadcast(deployer); + + distributionCreator.reallocateCampaignRewards(campaignId, froms, to); + + assertEq(distributionCreator.campaignReallocation(campaignId, froms[0]), to); + assertEq(distributionCreator.campaignListReallocation(campaignId, 0), froms[0]); + assertEq(distributionCreator.campaignListReallocation(campaignId, 1), froms[1]); + + vm.stopBroadcast(); + } +} diff --git a/scripts/foundry/UpdateCampaign.s.sol b/scripts/foundry/UpdateCampaign.s.sol new file mode 100644 index 0000000..c1bb952 --- /dev/null +++ b/scripts/foundry/UpdateCampaign.s.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; +import { DistributionCreator, DistributionParameters, CampaignParameters } from "contracts/DistributionCreator.sol"; +import { MockToken, IERC20 } from "contracts/mock/MockToken.sol"; +import { CommonUtils, ContractType } from "utils/src/CommonUtils.sol"; +import { CHAIN_BASE } from "utils/src/Constants.sol"; +import { StdAssertions } from "forge-std/Test.sol"; +import { ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; + +contract UpdateCampaign is CommonUtils, StdAssertions { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + /// TODO: COMPLETE + uint256 chainId = CHAIN_BASE; + IERC20 rewardToken = IERC20(0xC011882d0f7672D8942e7fE2248C174eeD640c8f); + uint256 amount = 97 ether; // after fees + bytes32 campaignId = 0x6628165d9b509afe46d9009fecc7012c68cc0ce24aafdc4ce11f23a01ccc1a22; + uint32 startTimestamp = uint32(1733155692); + uint32 duration = 3600 * 6; + /// END + + DistributionCreator distributionCreator = DistributionCreator( + _chainToContract(chainId, ContractType.DistributionCreator) + ); + uint32 timestamp = uint32(block.timestamp); + + // // Do some mint and deposit to change a lot reward distribution + // vm.startBroadcast(0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776); + // MockToken(address(0x0000206329b97DB379d5E1Bf586BbDB969C63274)).mint(deployer, 100_000 ether); + // vm.stopBroadcast(); + + vm.startBroadcast(deployer); + + // It will be less than that but we don't care + MockToken(address(rewardToken)).mint(deployer, amount); + rewardToken.approve(address(distributionCreator), amount); + + // IERC20(0x0000206329b97DB379d5E1Bf586BbDB969C63274).approve( + // address(0xbEEfa1aBfEbE621DF50ceaEF9f54FdB73648c92C), + // 100_000 ether + // ); + // ERC4626(0xbEEfa1aBfEbE621DF50ceaEF9f54FdB73648c92C).deposit(100_000 ether, deployer); + + // // ERC20 distrib change duration + // uint32 campaignType = 1; + // bytes memory campaignData = abi.encode( + // 0xbEEfa1aBfEbE621DF50ceaEF9f54FdB73648c92C, + // new address[](0), + // new address[](0), + // "", + // new bytes[](0), + // new bytes[](0), + // hex"" + // ); + + // ERC20 distrib + uint32 campaignType = 1; + bytes memory campaignData = abi.encode( + 0x70F796946eD919E4Bc6cD506F8dACC45E4539771, + new address[](0), + new address[](0), + "", + new bytes[](0), + new bytes[](0), + hex"" + ); + + // // Silo distrib + // address[] memory whitelist = new address[](1); + // whitelist[0] = 0x8095806d8753C0443C118D1C5e5eEC472e30BFeC; + // uint32 campaignType = 5; + // bytes memory campaignData = abi.encode( + // 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A, + // 2, + // 0xa42001D6d2237d2c74108FE360403C4b796B7170, + // whitelist, + // new address[](0), + // hex"" + // ); + + // // CLAMM distrib + // uint32 campaignType = 2; + // bytes memory campaignData = abi.encode( + // 0x5280d5E63b416277d0F81FAe54Bb1e0444cAbDAA, + // 5100, + // 1700, + // 3200, + // false, + // address(0), + // 1, + // new address[](0), + // new address[](0), + // "", + // new bytes[](0), + // hex"" + // ); + + distributionCreator.overrideCampaign( + campaignId, + CampaignParameters({ + campaignId: campaignId, + creator: deployer, + rewardToken: address(rewardToken), + amount: amount, + campaignType: campaignType, + startTimestamp: startTimestamp, + duration: duration, + campaignData: campaignData + }) + ); + + ( + , + address campaignCreator, + address campaignRewardToken, + uint256 campaignAmount, + uint256 campaignCampaignType, + uint32 campaignStartTimestamp, + uint32 campaignDuration, + bytes memory campaignCampaignData + ) = distributionCreator.campaignOverrides(campaignId); + assertEq(campaignCreator, deployer); + assertEq(campaignRewardToken, address(rewardToken)); + assertEq(campaignAmount, amount); + assertEq(campaignCampaignType, campaignType); + assertEq(campaignStartTimestamp, startTimestamp); + assertEq(campaignDuration, duration); + assertEq(campaignCampaignData, campaignData); + assertLt(distributionCreator.campaignOverridesTimestamp(campaignId, 0), timestamp); + assertLt(distributionCreator.campaignOverridesTimestamp(campaignId, 1), timestamp); + // assertLt(distributionCreator.campaignOverridesTimestamp(campaignId, 2), timestamp); + assertGe(distributionCreator.campaignOverridesTimestamp(campaignId, 2), timestamp); + vm.expectRevert(); + distributionCreator.campaignOverridesTimestamp(campaignId, 3); + + vm.stopBroadcast(); + } +} diff --git a/scripts/foundry/UpdateRoute.s.sol b/scripts/foundry/UpdateRoute.s.sol new file mode 100644 index 0000000..505adad --- /dev/null +++ b/scripts/foundry/UpdateRoute.s.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; +import { Distributor, MerkleTree } from "contracts/Distributor.sol"; +import { MockToken, IERC20 } from "contracts/mock/MockToken.sol"; +import { CommonUtils, ContractType } from "utils/src/CommonUtils.sol"; +import { CHAIN_BASE } from "utils/src/Constants.sol"; +import { StdAssertions } from "forge-std/Test.sol"; + +contract UpdateRoute is CommonUtils, StdAssertions { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + /// TODO: COMPLETE + uint256 chainId = CHAIN_BASE; + MerkleTree memory newTree = MerkleTree({ + merkleRoot: 0xb402de8ed2f573c780a39e6d41aa5276706c439849d1e4925d379f2aa8913577, + ipfsHash: bytes32(0) + }); + address updater = 0x435046800Fb9149eE65159721A92cB7d50a7534b; + /// END + + Distributor distributor = Distributor(_chainToContract(chainId, ContractType.Distributor)); + + vm.startBroadcast(updater); + + distributor.updateTree(newTree); + + vm.stopBroadcast(); + + // You then need to wait 1 hour to be effective + } +} diff --git a/scripts/foundry/UpgradeDistributionCreator.s.sol b/scripts/foundry/UpgradeDistributionCreator.s.sol new file mode 100644 index 0000000..0aa622f --- /dev/null +++ b/scripts/foundry/UpgradeDistributionCreator.s.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.17; + +import { console } from "forge-std/console.sol"; +import { DistributionCreator, DistributionParameters, CampaignParameters } from "contracts/DistributionCreator.sol"; +import { CommonUtils, ContractType } from "utils/src/CommonUtils.sol"; +import { Constants } from "utils/src/Constants.sol"; +import { StdAssertions } from "forge-std/Test.sol"; + +contract UpgradeDistributionCreator is CommonUtils, StdAssertions { + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + /// TODO: COMPLETE + uint256 chainId = Constants.CHAIN_BASE; + /// END + + DistributionCreator distributionCreator = DistributionCreator( + _chainToContract(chainId, ContractType.DistributionCreator) + ); + address governor = _chainToContract(chainId, ContractType.AngleLabsMultisig); + + vm.startBroadcast(deployer); + // We deploy the new implementation + address creatorImpl = address(new DistributionCreator()); + vm.stopBroadcast(); + + // Upgrade + vm.startBroadcast(governor); + distributionCreator.upgradeTo(address(creatorImpl)); + vm.stopBroadcast(); + + // Test storage + assertEq( + address(distributionCreator.accessControlManager()), + _chainToContract(chainId, ContractType.CoreMerkl) + ); + assertEq(address(distributionCreator.distributor()), _chainToContract(chainId, ContractType.Distributor)); + assertEq(distributionCreator.defaultFees(), 0.03e9); + assertEq( + distributionCreator.message(), + '" 1. Merkl is experimental software provided as is, use it at your own discretion. There may notably be delays in the onchain Merkle root updates and there may be flaws in the script (or engine) or in the infrastructure used to update results onchain. In that regard, everyone can permissionlessly dispute the rewards which are posted onchain, and when creating a distribution, you are responsible for checking the results and eventually dispute them. 2. If you are specifying an invalid pool address or a pool from an AMM that is not marked as supported, your rewards will not be taken into account and you will not be able to recover them. 3. If you do not blacklist liquidity position managers or smart contract addresses holding LP tokens that are not natively supported by the Merkl system, or if you don\'t specify the addresses of the liquidity position managers that are not automatically handled by the system, then the script will not be able to take the specifities of these addresses into account, and it will reward them like a normal externally owned account would be. If these are smart contracts that do not support external rewards, then rewards that should be accruing to it will be lost. 4. If rewards sent through Merkl remain unclaimed for a period of more than 1 year after the end of the distribution (because they are meant for instance for smart contract addresses that cannot claim or deal with them), then we reserve the right to recover these rewards. 5. Fees apply to incentives deposited on Merkl, unless the pools incentivized contain a whitelisted token (e.g an Angle Protocol stablecoin). 6. By interacting with the Merkl smart contract to deposit an incentive for a pool, you are exposed to smart contract risk and to the offchain mechanism used to compute reward distribution. 7. If the rewards you are sending are too small in value, or if you are sending rewards using a token that is not approved for it, your rewards will not be handled by the script, and they may be lost. 8. If you mistakenly send too much rewards compared with what you wanted to send, you will not be able to call them back. You will also not be able to prematurely end a reward distribution once created. 9. The engine handling reward distribution for a pool may not look at all the swaps occurring on the pool during the time for which you are incentivizing, but just at a subset of it to gain in efficiency. Overall, if you distribute incentives using Merkl, it means that you are aware of how the engine works, of the approximations it makes and of the behaviors it may trigger (e.g. just in time liquidity). 10. Rewards corresponding to incentives distributed through Merkl do not compound block by block, but are regularly made available (through a Merkle root update) at a frequency which depends on the chain. "' + ); + assertEq(distributionCreator.messageHash(), 0x08dabc24dcfcb230453d08bce47c730ed6f1cce205bc153680488959b503644e); + { + (bytes32 campaignId, , , , , , , , , , , , ) = distributionCreator.distributionList(0); + assertEq(campaignId, 0xb3fc2abc303c70a16ab9d5fc38d7e8aeae66593a87a3d971b024dd34b97e94b1); + } + { + (bytes32 campaignId, , , , , , , , , , , , ) = distributionCreator.distributionList(73); + assertEq(campaignId, 0x157a32c11ce34030465e1c28c309f38c18161028355f3446f54b677d11ceb63a); + } + assertEq(distributionCreator.feeRebate(0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185), 0); + assertEq(distributionCreator.isWhitelistedToken(_chainToContract(chainId, ContractType.AgEUR)), 1); + assertEq(distributionCreator._nonces(0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185), 4); + assertEq( + distributionCreator.userSignatures(0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185), + 0x08dabc24dcfcb230453d08bce47c730ed6f1cce205bc153680488959b503644e + ); + assertEq(distributionCreator.userSignatureWhitelist(0xfdA462548Ce04282f4B6D6619823a7C64Fdc0185), 0); + + assertEq(distributionCreator.rewardTokens(0), 0x7D49a065D17d6d4a55dc13649901fdBB98B2AFBA); + assertEq(distributionCreator.rewardTokens(21), 0xF734eFdE0C424BA2B547b186586dE417b0954802); + assertEq(distributionCreator.rewardTokenMinAmounts(0x7D49a065D17d6d4a55dc13649901fdBB98B2AFBA), 1 ether); + + { + (bytes32 campaignId, , , , , , , ) = distributionCreator.campaignList(0); + assertEq(campaignId, 0x4e2bf13f682a244a80e0f25e1545fc8ad3a181d60658d22a3d347ee493e2a740); + } + { + (bytes32 campaignId, , , , , , , ) = distributionCreator.campaignList(67); + assertEq(campaignId, 0xf7d416acc480a41cd4cbb1bd68941f2f585adb659bd95d45e193589175356972); + } + assertEq(distributionCreator.campaignSpecificFees(4), 0.005e9); + + { + (bytes32 campaignId, , , , , , , ) = distributionCreator.campaignOverrides( + 0xf7d416acc480a41cd4cbb1bd68941f2f585adb659bd95d45e193589175356972 + ); + assertEq(campaignId, bytes32(0)); + } + + vm.expectRevert(); + distributionCreator.campaignOverridesTimestamp( + 0x4e2bf13f682a244a80e0f25e1545fc8ad3a181d60658d22a3d347ee493e2a740, + 0 + ); + } +}