Created
April 3, 2022 15:12
-
-
Save raddy/e65bd5bba305e0ff685b2d668fd8bad3 to your computer and use it in GitHub Desktop.
Find allowances slot in an ERC20
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
#!/usr/bin/env node | |
const { ethers } = require("ethers"); | |
const { formatUnits } = require("@ethersproject/units"); | |
const { program } = require("commander"); | |
const erc20Abi = require("./erc20.json"); | |
const storage = async ( | |
provider, | |
tokenAddress, | |
k | |
) => { | |
return ethers.BigNumber.from( | |
await provider.getStorageAt( | |
tokenAddress, | |
k | |
) | |
); | |
}; | |
const key = async ( | |
user, | |
contractAddress, | |
slot | |
) => { | |
const innerCalc = ethers.utils.solidityKeccak256( | |
["uint256", "uint256"], | |
[user, slot] | |
); | |
return ethers.utils.solidityKeccak256( | |
["uint256", "uint256"], | |
[contractAddress, innerCalc] | |
); | |
}; | |
const search = async ( | |
user, | |
tokenAddress, | |
target, | |
rpc, | |
max | |
) => { | |
const provider = new ethers.providers.JsonRpcProvider(rpc); | |
const token = new ethers.Contract(tokenAddress, erc20Abi, provider); | |
let tokenSymbol = tokenAddress; | |
let tokenDecimals = 18; | |
try { | |
tokenDecimals = await token.decimals(); | |
tokenSymbol = await token.symbol(); | |
} catch (e) { | |
// Fuck it | |
} | |
// Get the actual allowance | |
const allowance = await token.allowance(user, target); | |
console.log(`User ${user} approved ${formatUnits( | |
allowance, | |
tokenDecimals | |
)} ${tokenSymbol} tokens to ${target}`); | |
for (let i = 0; i <= max; i++) { | |
const k = await key(user, target, i); | |
const v = await storage(provider, tokenAddress, k); | |
if (v.eq(allowance)) { | |
console.log(`Slot ${i} of ${tokenSymbol} matches ${allowance}`); | |
return; | |
} | |
else { | |
console.log(`Slot ${i} of ${tokenSymbol} did not match ${allowance}`) | |
} | |
} | |
}; | |
const main = async () => { | |
program | |
.argument("<user>", "User address") | |
.argument("<token>", "ERC20 token") | |
.argument("<target>", "Target Contract") | |
.option("--rpc <url>", "RPC URL", "http://127.0.0.1:8545") | |
.option("--max <max>", "Max Slot to Test", 15) | |
.action(async (user, tokenAddress, target, options) => { | |
await search( | |
user, | |
tokenAddress, | |
target, | |
options.rpc, | |
parseInt(options.max) | |
); | |
}); | |
program.parseAsync(); | |
}; | |
main().catch((e) => console.error(e.toString())); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment