Skip to content

Commit

Permalink
feat: allow retroactive campaign (#93)
Browse files Browse the repository at this point in the history
* feat: allow retroactive campaign

* add fork bash script

* test retroactive campaigns

* chore: remove redundant scripts

* remove forge std

* remove utils lib
  • Loading branch information
GuillaumeNervoXS authored Dec 13, 2024
1 parent bbdc427 commit 9135657
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 44 deletions.
2 changes: 0 additions & 2 deletions contracts/DistributionCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -524,8 +524,6 @@ contract DistributionCreator is UUPSHelper, ReentrancyGuardUpgradeable {
/// @notice Internal version of `createCampaign`
function _createCampaign(CampaignParameters memory newCampaign) internal returns (bytes32) {
uint256 rewardTokenMinAmount = rewardTokenMinAmounts[newCampaign.rewardToken];
// if epoch parameters lead to a past campaign
if (newCampaign.startTimestamp < block.timestamp) revert Errors.CampaignShouldStartInFuture();
// if the campaign doesn't last at least one hour
if (newCampaign.duration < HOUR) revert Errors.CampaignDurationBelowHour();
// if the reward token is not whitelisted as an incentive token
Expand Down
8 changes: 5 additions & 3 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ["*"]
Expand All @@ -20,7 +20,7 @@ runs = 500
runs = 500

[profile.dev]
via_ir = false
via_ir = true

[rpc_endpoints]
localhost = "${LOCALHOST_NODE_URI}"
Expand Down Expand Up @@ -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" }
Expand Down Expand Up @@ -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/" }
swell = { chainId = 1923, key = "${SWELL_ETHERSCAN_API_KEY}", url = "https://explorer.swellnetwork.io:443/api/" }
46 changes: 46 additions & 0 deletions helpers/fork.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#! /bin/bash

source lib/utils/helpers/common.sh

function main {
if [ ! -f .env ]; then
echo ".env not found!"
exit 1
fi
source .env

echo "Which chain would you like to fork ?"
echo "- 1: Ethereum Mainnet"
echo "- 2: Arbitrum"
echo "- 3: Polygon"
echo "- 4: Gnosis"
echo "- 5: Avalanche"
echo "- 6: Base"
echo "- 7: Binance Smart Chain"
echo "- 8: Celo"
echo "- 9: Polygon ZkEvm"
echo "- 10: Optimism"
echo "- 11: Linea"

read option

uri=$(chain_to_uri $option)
if [ -z "$uri" ]; then
echo "Unknown network"
exit 1
fi

echo "What block do you want to fork ? (Can leave empty for instant)"

read block

if [ -z "$block" ]; then
echo "Forking $uri"
anvil --fork-url $uri
else
echo "Forking $uri at block $block"
anvil --fork-url $uri --fork-block-number $block
fi
}

main
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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}'"
},
Expand All @@ -36,4 +41,4 @@
"forge-std": "github:foundry-rs/forge-std#v1.9.4"
},
"dependencies": {}
}
}
4 changes: 3 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
@openzeppelin/=node_modules/@openzeppelin/
forge-std/=node_modules/forge-std/src
forge-std/=node_modules/forge-std/src
oz/=node_modules/@openzeppelin/contracts/
utils/=lib/utils
94 changes: 57 additions & 37 deletions test/unit/DistributionCreator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -127,28 +127,6 @@ contract Test_DistributionCreator_Initialize is DistributionCreatorTest {
}

contract Test_DistributionCreator_CreateDistribution is DistributionCreatorTest {
function test_RevertWhen_CampaignShouldStartInFuture() public {
DistributionParameters memory distribution = DistributionParameters({
uniV3Pool: address(pool),
rewardToken: address(angle),
positionWrappers: positionWrappers,
wrapperTypes: wrapperTypes,
amount: 1e10,
propToken0: 4000,
propToken1: 2000,
propFees: 4000,
isOutOfRangeIncentivized: 0,
epochStart: uint32(block.timestamp - 1),
numEpoch: 25,
boostedReward: 0,
boostingAddress: address(0),
rewardId: keccak256("TEST"),
additionalData: hex""
});
vm.expectRevert(Errors.CampaignShouldStartInFuture.selector);
creator.createDistribution(distribution);
}

function test_RevertWhen_CampaignDurationIsZero() public {
DistributionParameters memory distribution = DistributionParameters({
uniV3Pool: address(pool),
Expand Down Expand Up @@ -356,21 +334,6 @@ contract Test_DistributionCreator_CreateDistributions is DistributionCreatorTest
}

contract Test_DistributionCreator_CreateCampaign is DistributionCreatorTest {
function test_RevertWhen_CampaignShouldStartInFuture() public {
CampaignParameters memory campaign = CampaignParameters({
campaignId: keccak256("TEST"),
creator: address(0),
campaignData: hex"ab",
rewardToken: address(angle),
amount: 1e10,
campaignType: 0,
startTimestamp: uint32(block.timestamp - 1),
duration: 3600
});
vm.expectRevert(Errors.CampaignShouldStartInFuture.selector);
creator.createCampaign(campaign);
}

function test_RevertWhen_CampaignDurationIsZero() public {
CampaignParameters memory campaign = CampaignParameters({
campaignId: keccak256("TEST"),
Expand Down Expand Up @@ -479,6 +442,63 @@ contract Test_DistributionCreator_CreateCampaign is DistributionCreatorTest {
assertEq(extraData, fetchedCampaignData);
assertEq(campaignId, fetchedCampaignId);
}

function test_Succeed_CampaignStartInThePast() public {
uint256 amount = 1e8;
CampaignParameters memory campaign = CampaignParameters({
campaignId: keccak256("TEST"),
creator: address(0),
campaignData: hex"ab",
rewardToken: address(angle),
amount: amount,
campaignType: 0,
startTimestamp: uint32(block.timestamp - 1),
duration: 3600
});

vm.prank(alice);
creator.createCampaign(campaign);

address[] memory whitelist = new address[](1);
whitelist[0] = alice;
address[] memory blacklist = new address[](1);
blacklist[0] = charlie;

bytes memory extraData = hex"ab";

// Additional asserts to check for correct behavior
bytes32 campaignId = bytes32(
keccak256(
abi.encodePacked(
block.chainid,
alice,
address(campaign.rewardToken),
uint32(campaign.campaignType),
uint32(campaign.startTimestamp),
uint32(campaign.duration),
campaign.campaignData
)
)
);
(
bytes32 fetchedCampaignId,
address fetchedCreator,
address fetchedRewardToken,
uint256 fetchedAmount,
uint32 fetchedCampaignType,
uint32 fetchedStartTimestamp,
uint32 fetchedDuration,
bytes memory fetchedCampaignData
) = creator.campaignList(creator.campaignLookup(campaignId));
assertEq(alice, fetchedCreator);
assertEq((amount * 9) / 10, fetchedAmount); // amount minus 10% fees
assertEq(address(angle), fetchedRewardToken);
assertEq(campaign.campaignType, fetchedCampaignType);
assertEq(campaign.startTimestamp, fetchedStartTimestamp);
assertEq(campaign.duration, fetchedDuration);
assertEq(extraData, fetchedCampaignData);
assertEq(campaignId, fetchedCampaignId);
}
}

contract Test_DistributionCreator_CreateCampaigns is DistributionCreatorTest {
Expand Down

0 comments on commit 9135657

Please sign in to comment.