Skip to content

Commit

Permalink
feat(TwabRewards): add exploit scenario to fork
Browse files Browse the repository at this point in the history
  • Loading branch information
PierrickGT committed Jun 3, 2022
1 parent 3d3118a commit 6133a04
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 71 deletions.
4 changes: 2 additions & 2 deletions scripts/fork/distribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default task("fork:distribute", "Distribute Ether and USDC").setAction(

const { ethers } = hre;
const { provider, getContractAt, getSigners } = ethers;
const [deployer, wallet2] = await getSigners();
const [deployer, attacker] = await getSigners();

const ethHolder = provider.getUncheckedSigner(ETH_HOLDER_ADDRESS_MAINNET);
const poolHolder = provider.getUncheckedSigner(POOL_HOLDER_ADDRESS_MAINNET);
Expand All @@ -28,7 +28,7 @@ export default task("fork:distribute", "Distribute Ether and USDC").setAction(

const recipients: { [key: string]: string } = {
["Deployer"]: deployer.address,
["Wallet 2"]: wallet2.address,
["Attacker"]: attacker.address,
};

const keys = Object.keys(recipients);
Expand Down
219 changes: 150 additions & 69 deletions scripts/fork/twabRewards.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,76 @@
import { getContractFactory } from "@nomiclabs/hardhat-ethers/types";
import { usdc } from "@studydefi/money-legos/erc20";
import { subtask, task, types } from "hardhat/config";

import {
POOL_TOKEN_ADDRESS_MAINNET,
POOL_TOKEN_DECIMALS,
} from "../../Constants";
import { POOL_TOKEN_ADDRESS_MAINNET, POOL_TOKEN_DECIMALS } from "../../Constants";

import { action, info, success } from "../../helpers";
import {
increaseTime as increaseTimeUtil,
} from "../../test/utils/increaseTime";
import { increaseTime as increaseTimeUtil } from "../../test/utils/increaseTime";

export default task("fork:twab-rewards", "Run TWAB Rewards fork").setAction(
async (taskArguments, hre) => {
action("Run TWAB Rewards fork...");

const { ethers, getNamedAccounts, run } = hre;
const { ethers, run } = hre;

const { getContractAt, provider, utils } = ethers;
const { deployer } = await getNamedAccounts();
const { getContractAt, getSigners, provider } = ethers;
const [deployer, attacker] = await getSigners();

const increaseTime = (time: number) => increaseTimeUtil(provider, time);

info(`Deployer is: ${deployer}`);
info(`Deployer is: ${deployer.address}`);

const prizePoolAddress = await run("fork:create-pool");

const prizePool = await getContractAt("YieldSourcePrizePool", prizePoolAddress);
const ticketAddress = await prizePool.getTicket();

const twabRewardsAddress = await run("deploy-twab-rewards", { ticketAddress });
const promotionId = (await run("create-promotion", { twabRewardsAddress })).toNumber();

const firstPromotionId = (
await run("create-promotion", {
twabRewardsAddress,
signer: deployer,
tokensPerEpoch: "1000",
epochDuration: 604800,
numberOfEpochs: 12,
})
).toNumber();

const secondPromotionId = (
await run("create-promotion", {
twabRewardsAddress,
signer: attacker,
tokensPerEpoch: "12000",
epochDuration: 60,
numberOfEpochs: 1,
})
).toNumber();

await run("deposit-into-prize-pool", { prizePoolAddress });

// Attacker ends promotion and receives back is initial deposit
await run("end-promotion", {
twabRewardsAddress,
promotionId: secondPromotionId,
signer: attacker,
});

// We move time 6 months forward
await increaseTime(15778458);

await run("claim-rewards", { twabRewardsAddress, promotionId });
await run("destroy-promotion", { twabRewardsAddress, promotionId });
// Attacker should not be able to steal funds from first promotion
await run("destroy-promotion", {
twabRewardsAddress,
promotionId: secondPromotionId,
signer: attacker,
});

await run("claim-rewards", { twabRewardsAddress, promotionId: firstPromotionId });
await run("destroy-promotion", {
twabRewardsAddress,
promotionId: firstPromotionId,
signer: deployer,
});
}
);

Expand Down Expand Up @@ -67,45 +98,58 @@ subtask("deploy-twab-rewards", "Deploy TWAB Rewards")

subtask("create-promotion", "Create TWAB Rewards promotion")
.addParam("twabRewardsAddress", "TWAB Rewards address")
.setAction(async ({ twabRewardsAddress }, { ethers }) => {
action("Create TWAB Rewards promotion...");

const { getContractAt, getSigners, provider, utils } = ethers;
const { getTransactionReceipt } = provider;
const { parseUnits } = utils;

const [deployer] = await getSigners();
const twabRewards = await getContractAt("TwabRewards", twabRewardsAddress);
const poolContract = await getContractAt(usdc.abi, POOL_TOKEN_ADDRESS_MAINNET, deployer);

await poolContract.approve(twabRewardsAddress, parseUnits("12000", POOL_TOKEN_DECIMALS));

const createPromotionTx = await twabRewards.createPromotion(
POOL_TOKEN_ADDRESS_MAINNET,
(await provider.getBlock('latest')).timestamp,
parseUnits("1000", POOL_TOKEN_DECIMALS),
604800,
12
);

const createPromotionTxReceipt = await getTransactionReceipt(createPromotionTx.hash);

const createPromotionTxEvents = createPromotionTxReceipt.logs.map((log: any) => {
try {
return twabRewards.interface.parseLog(log);
} catch (e) {
return null;
}
});

const promotionCreatedEvent = createPromotionTxEvents.find(
(event: any) => event && event.name === "PromotionCreated"
);

success("TWAB Rewards promotion created!");

return promotionCreatedEvent?.args['promotionId'];
});
.addParam("signer", "Transaction signer", null, types.any)
.addParam("tokensPerEpoch", "Number of tokens per epoch")
.addParam("epochDuration", "Duration of an epoch in seconds", null, types.float)
.addParam("numberOfEpochs", "Number of epochs", null, types.float)
.setAction(
async (
{ twabRewardsAddress, signer, tokensPerEpoch, epochDuration, numberOfEpochs },
{ ethers }
) => {
action("Create TWAB Rewards promotion...");

const { getContractAt, provider, utils } = ethers;
const { getTransactionReceipt } = provider;
const { parseUnits } = utils;

const twabRewards = await getContractAt("TwabRewards", twabRewardsAddress, signer);
const poolContract = await getContractAt(usdc.abi, POOL_TOKEN_ADDRESS_MAINNET, signer);

await poolContract.approve(
twabRewardsAddress,
parseUnits("12000", POOL_TOKEN_DECIMALS)
);

const createPromotionTx = await twabRewards.createPromotion(
POOL_TOKEN_ADDRESS_MAINNET,
(
await provider.getBlock("latest")
).timestamp,
parseUnits(tokensPerEpoch, POOL_TOKEN_DECIMALS),
epochDuration,
numberOfEpochs
);

const createPromotionTxReceipt = await getTransactionReceipt(createPromotionTx.hash);

const createPromotionTxEvents = createPromotionTxReceipt.logs.map((log: any) => {
try {
return twabRewards.interface.parseLog(log);
} catch (e) {
return null;
}
});

const promotionCreatedEvent = createPromotionTxEvents.find(
(event: any) => event && event.name === "PromotionCreated"
);

success("TWAB Rewards promotion created!");

return promotionCreatedEvent?.args["promotionId"];
}
);

subtask("deposit-into-prize-pool", "Deposit into prize pool")
.addParam("prizePoolAddress", "Prize pool address")
Expand All @@ -115,18 +159,23 @@ subtask("deposit-into-prize-pool", "Deposit into prize pool")
const { getContractAt, getSigners, utils } = ethers;
const { parseUnits } = utils;

const [deployer, wallet2] = await getSigners();
const [deployer, attacker] = await getSigners();
const prizePool = await getContractAt("YieldSourcePrizePool", prizePoolAddress);
const usdcContract = await getContractAt(usdc.abi, usdc.address);

const depositAmountDeployer = parseUnits("750", usdc.decimals);
await usdcContract.connect(deployer).approve(prizePoolAddress, depositAmountDeployer);

const depositAmountWallet2 = parseUnits("250", usdc.decimals);
await usdcContract.connect(wallet2).approve(prizePoolAddress, depositAmountWallet2);
await usdcContract.connect(attacker).approve(prizePoolAddress, depositAmountWallet2);

await prizePool.connect(deployer).depositToAndDelegate(deployer.address, depositAmountDeployer, deployer.address);
await prizePool.connect(wallet2).depositToAndDelegate(wallet2.address, depositAmountWallet2, wallet2.address);
await prizePool
.connect(deployer)
.depositToAndDelegate(deployer.address, depositAmountDeployer, deployer.address);

await prizePool
.connect(attacker)
.depositToAndDelegate(attacker.address, depositAmountWallet2, attacker.address);

success("Successfully deposited into the prize pool!");
});
Expand Down Expand Up @@ -164,28 +213,25 @@ subtask("claim-rewards", "Claim rewards")
(event: any) => event && event.name === "RewardsClaimed"
);

const rewardsClaimedAmount = formatEther(rewardsClaimedEvent?.args['amount']);
const rewardsClaimedAmount = formatEther(rewardsClaimedEvent?.args["amount"]);

success(`Successfully claimed ${rewardsClaimedAmount} POOL!`);
});

subtask("destroy-promotion", "Destroy promotion")
.addParam("twabRewardsAddress", "TWAB Rewards address")
.addParam("promotionId", "Id of the promotion", null, types.float)
.setAction(async ({ twabRewardsAddress, promotionId }, { ethers }) => {
action("Claim rewards...");
.addParam("signer", "Transaction signer", null, types.any)
.setAction(async ({ twabRewardsAddress, promotionId, signer }, { ethers }) => {
action("Destroy promotion...");

const { getContractAt, getSigners, provider, utils } = ethers;
const { getTransactionReceipt } = provider;
const { formatEther } = utils;

const [deployer] = await getSigners();
const twabRewards = await getContractAt("TwabRewards", twabRewardsAddress, deployer);
const twabRewards = await getContractAt("TwabRewards", twabRewardsAddress, signer);

const destroyPromotionTx = await twabRewards.destroyPromotion(
promotionId,
deployer.address,
);
const destroyPromotionTx = await twabRewards.destroyPromotion(promotionId, signer.address);

const destroyPromotionReceipt = await getTransactionReceipt(destroyPromotionTx.hash);

Expand All @@ -201,7 +247,42 @@ subtask("destroy-promotion", "Destroy promotion")
(event: any) => event && event.name === "PromotionDestroyed"
);

const promotionDestroyedAmount = formatEther(promotionDestroyedEvent?.args['amount']);
const promotionDestroyedAmount = formatEther(promotionDestroyedEvent?.args["amount"]);

success(
`Successfully destroyed promotion and received ${promotionDestroyedAmount} POOL back!`
);
});

subtask("end-promotion", "End promotion")
.addParam("twabRewardsAddress", "TWAB Rewards address")
.addParam("promotionId", "Id of the promotion", null, types.float)
.addParam("signer", "Transaction signer", null, types.any)
.setAction(async ({ twabRewardsAddress, promotionId, signer }, { ethers }) => {
action("End promotion...");

const { getContractAt, provider, utils } = ethers;
const { getTransactionReceipt } = provider;
const { formatEther } = utils;

const twabRewards = await getContractAt("TwabRewards", twabRewardsAddress, signer);

const endPromotionTx = await twabRewards.endPromotion(promotionId, signer.address);
const endPromotionReceipt = await getTransactionReceipt(endPromotionTx.hash);

const endPromotionEvents = endPromotionReceipt.logs.map((log: any) => {
try {
return twabRewards.interface.parseLog(log);
} catch (e) {
return null;
}
});

const promotionEndedEvent = endPromotionEvents.find(
(event: any) => event && event.name === "PromotionEnded"
);

const promotionEndedAmount = formatEther(promotionEndedEvent?.args["amount"]);

success(`Successfully destroyed promotion and received ${promotionDestroyedAmount} POOL back!`);
success(`Successfully ended promotion and received ${promotionEndedAmount} POOL back!`);
});

0 comments on commit 6133a04

Please sign in to comment.