Whoever said the standard ABI for ERC20 is enough to interact with any ERC20 must have meant generally. And you are right to say that if you use a standard ERC20 ABI you won't be able to call myCustomFunction()
. I mean technically you can, and you don't even need an ABI to interact with a contract if you know its functions signatures (the 4 bytes signature).
In an ABI you have the definitions of some functions you can use along with the types of parameters they expect. In a Standard ERC20 ABI, I'd expect to find transfer
, approve
, balanceOf
and such.
If you know what parameters myCustomFunction
takes, it's pretty trivial to add it. Look for any function definition inside your ABI (it's just JSON, it doesn't bite) and copy the format
Wait, what's a 4-byte signature?
Transactions on the Ethereum blockchain are all the same thing: they have a caller (address
), a callee (address
), some data (bytes
) and some value (uint256
). In that sense, look at all transactions wether it is calling a function on a smart contract, sending ETH to a friend, sending a string message, or sending nothing as relatively the same thing.
What diferenciates contract functions call is that when you call a smart contract, it can read the data you send and it can do stuff with it like computing, storing, calling other addresses with value and data or reverting the transaction.
For any transaction that calls a function on a smart contract, by convention, the first 4 bytes of the call data contain what's called a selector or a function signature. The 4 bytes are obtained by hashing the function signature and truncating. for example for the function:
function transfer(address to, amount uint256) external {
// ...
}
You would hash the string transfer(address,amount)
with keccak256 and you would get a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b
The first 4 bytes are
therefore the selector 0xa9059cbb
corresponds to the transfer
function. If you look at any call of transfer(address,uint256)
in Etherscan and look at the raw calldata you'll see it starts with that selector.
if you use a library like ethers
in JS to interact with contracts, what it does with the ABI is compute the selector from the function name and argument types and it builds the calldata from that.