Check if permit method is supported in ERC20 token

Is there a way to check if a certain ERC20 contract address supports the ERC2612 permit method?

1 Like

First thing that comes to mind is checking if the contract implements one of these functions:

  • function nonces(address) public view returns (uint256)
  • function DOMAIN_SEPARATOR() external view returns (bytes32)

Or perhaps even both of these functions, for better assurance.

It is not clear whether you want to conduct this check onchain of offchain, but both options are viable.

2 Likes

I want to do an offchain check.

Can I do this check with only the target contract address ?

1 Like

Yes, for example, using web3.js, you, can do something like:

const Web3 = require("web3");

const get_selector = func_signature => Web3.utils.keccak256(func_signature).slice(2, 10);

const func1_selector = get_selector("nonces(address)");
const func2_selector = get_selector("DOMAIN_SEPARATOR()");

async function run() {
    const web3 = new Web3(YOUR_NODE_URL);
    const bytecode = await web3.eth.getCode(YOUR_CONTRACT_ADDRESS);

    if (bytecode.includes(func1_selector) && bytecode.includes(func2_selector))
        console.log("ERC2612 supported");
    else
        console.log("ERC2612 not supported");

    if (web3.currentProvider.disconnect)
        web3.currentProvider.disconnect();
}

run();
2 Likes
  let byteCode = await provider.getCode(tokenAddress);

Why don't i get the exact bytecode from my contract. I get a byteCode that's partially the same.

This is the contract code, if you scroll to the bottom you'll find contract creation code.

This is what byteCode returns. My provider is connected to Avalanche Fuji

0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b41146101a8578063a9059cbb146101b0578063d505accf146101c3578063dd62ed3e146101d857600080fd5b806370a08231146101515780637ecebe001461017a57806384b0196e1461018d57600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd14610127578063313ce5671461013a5780633644e51514610149575b600080fd5b6100dc610211565b6040516100e99190610c62565b60405180910390f35b610105610100366004610c98565b6102a3565b60405190151581526020016100e9565b6002545b6040519081526020016100e9565b610105610135366004610cc2565b6102bd565b604051601281526020016100e9565b6101196102e1565b61011961015f366004610cfe565b6001600160a01b031660009081526020819052604090205490565b610119610188366004610cfe565b6102f0565b61019561030e565b6040516100e99796959493929190610d19565b6100dc610354565b6101056101be366004610c98565b610363565b6101d66101d1366004610daf565b610371565b005b6101196101e6366004610e22565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60606003805461022090610e55565b80601f016020809104026020016040519081016040528092919081815260200182805461024c90610e55565b80156102995780601f1061026e57610100808354040283529160200191610299565b820191906000526020600020905b81548152906001019060200180831161027c57829003601f168201915b5050505050905090565b6000336102b18185856104b0565b60019150505b92915050565b6000336102cb8582856104c2565b6102d6858585610540565b506001949350505050565b60006102eb61059f565b905090565b6001600160a01b0381166000908152600760205260408120546102b7565b6000606080600080600060606103226106ca565b61032a6106f7565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004805461022090610e55565b6000336102b1818585610540565b8342111561039a5760405163313c898160e11b8152600481018590526024015b60405180910390fd5b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886103e78c6001600160a01b0316600090815260076020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061044282610724565b9050600061045282878787610751565b9050896001600160a01b0316816001600160a01b031614610499576040516325c0072360e11b81526001600160a01b0380831660048301528b166024820152604401610391565b6104a48a8a8a6104b0565b50505050505050505050565b6104bd838383600161077f565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461053a578181101561052b57604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610391565b61053a8484848403600061077f565b50505050565b6001600160a01b03831661056a57604051634b637e8f60e11b815260006004820152602401610391565b6001600160a01b0382166105945760405163ec442f0560e01b815260006004820152602401610391565b6104bd838383610854565b6000306001600160a01b037f000000000000000000000000dcfb5f804dfb67a0fa1c4c802b5d2ad4269618d8161480156105f857507f000000000000000000000000000000000000000000000000000000000000a86946145b1561062257507f7809badea574e77b95db083a8e78900c3fc657049296da38e519e2409f7480c890565b6102eb604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f245c734e6d4ec044daf7beffa09d54d4bafba490113c199734d790b04a7390e5918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60606102eb7f4d79546f6b656e00000000000000000000000000000000000000000000000007600561097e565b60606102eb7f3100000000000000000000000000000000000000000000000000000000000001600661097e565b60006102b761073161059f565b8360405161190160f01b8152600281019290925260228201526042902090565b60008060008061076388888888610a29565b9250925092506107738282610af8565b50909695505050505050565b6001600160a01b0384166107a95760405163e602df0560e01b815260006004820152602401610391565b6001600160a01b0383166107d357604051634a1406b160e11b815260006004820152602401610391565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561053a57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161084691815260200190565b60405180910390a350505050565b6001600160a01b03831661087f5780600260008282546108749190610e8f565b909155506108f19050565b6001600160a01b038316600090815260208190526040902054818110156108d25760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610391565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b03821661090d5760028054829003905561092c565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161097191815260200190565b60405180910390a3505050565b606060ff83146109985761099183610bb5565b90506102b7565b8180546109a490610e55565b80601f01602080910402602001604051908101604052809291908181526020018280546109d090610e55565b8015610a1d5780601f106109f257610100808354040283529160200191610a1d565b820191906000526020600020905b815481529060010190602001808311610a0057829003601f168201915b505050505090506102b7565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115610a645750600091506003905082610aee565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015610ab8573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610ae457506000925060019150829050610aee565b9250600091508190505b9450945094915050565b6000826003811115610b0c57610b0c610eb0565b03610b15575050565b6001826003811115610b2957610b29610eb0565b03610b475760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115610b5b57610b5b610eb0565b03610b7c5760405163fce698f760e01b815260048101829052602401610391565b6003826003811115610b9057610b90610eb0565b03610bb1576040516335e2f38360e21b815260048101829052602401610391565b5050565b60606000610bc283610bf4565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b600060ff8216601f8111156102b757604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b81811015610c4257602081850181015186830182015201610c26565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610c756020830184610c1c565b9392505050565b80356001600160a01b0381168114610c9357600080fd5b919050565b60008060408385031215610cab57600080fd5b610cb483610c7c565b946020939093013593505050565b600080600060608486031215610cd757600080fd5b610ce084610c7c565b9250610cee60208501610c7c565b9150604084013590509250925092565b600060208284031215610d1057600080fd5b610c7582610c7c565b60ff60f81b881681526000602060e081840152610d3960e084018a610c1c565b8381036040850152610d4b818a610c1c565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015610d9d57835183529284019291840191600101610d81565b50909c9b505050505050505050505050565b600080600080600080600060e0888a031215610dca57600080fd5b610dd388610c7c565b9650610de160208901610c7c565b95506040880135945060608801359350608088013560ff81168114610e0557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215610e3557600080fd5b610e3e83610c7c565b9150610e4c60208401610c7c565b90509250929050565b600181811c90821680610e6957607f821691505b602082108103610e8957634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156102b757634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fdfea2646970667358221220629d3ddcff0250b60e07302e0154a11403ca92d4d4bc29704178f9343aae58d264736f6c63430008150033
1 Like

That's not the same as contract byte code.

The contract creation code includes the input passed upon contract deployment (i.e., the input values passed to the contract's constructor).

The contract byte code does not.

2 Likes
import { keccak256 } from 'js-sha3';

const get_selector = (functionSelector: string) => keccak256(functionSelector).slice(2, 10);
  const func1_selector = get_selector("nonces(address)");
  const func2_selector = get_selector("DOMAIN_SEPARATOR()");

// func1_selector & func2_selector
-------------------- cebe00bc -------------------
-------------------- 44e51572 -------------------

I didn't get back the hash included in byteCode. I'm sure the contract has these functions.

1 Like

I get different selectors, namely:

  1. 7ecebe00
  2. 3644e515

Both of them are in your contract's bytecode.

So the js-sha3 library is probably incompatible with what we're trying to do here.

BTW, the interface also declares function permit:

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

So you might want to extend the script to check the presence of that function as well:

const func3_selector = get_selector("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)");

async function run() {
    ...
    if (
        bytecode.includes(func1_selector) &&
        bytecode.includes(func2_selector) &&
        bytecode.includes(func3_selector)
    )
    ...
}
2 Likes

Yeah I tried using the ethers.utils.keccak256 but it completely fails.

I wonder what the web3 library does under the hood to make it work like that

Appreciate you looking into this! :cowboy_hat_face:

1 Like

Change this:

ethers.utils.keccak256("yourInputString")

To this:

ethers.utils.keccak256(ethers.utils.toUtf8Bytes("yourInputString"))

Or to this:

ethers.utils.solidityKeccak256(["string"], ["yourInputString"])
1 Like

Thanks alot @barakman. This worked perfectly with ethers.

 const func1_selector = ethers.utils.solidityKeccak256(['string'], ["nonces(address)"]).slice(2, 10);
 const func2_selector = ethers.utils.solidityKeccak256(['string'], ["DOMAIN_SEPARATOR()"]).slice(2, 10);
 const func3_selector = ethers.utils.solidityKeccak256(['string'], ["permit(address,address,uint256,uint256,uint8,bytes32,bytes32)"]).slice(2, 10);
1 Like