How to call another smart contract
A guide to calling a smart contract within another contract
Table of Contents for How to call another smart contract
If you have 2 smart contracts. One has a public function, and you want to call that function from another.
Let’s say you have this contract, deployed already:
contract FirstDeployedContract {
uint public counter = 1;
function setCounter(uint _newValue) public {
counter = _newValue;
}
}
And you have a new contract where you want to call setCount(123)
.
There are a few ways you could do this…
If you know the other (deployed) contract you can cast an address
to that contract type, which means you can then call the function setCounter(uint)
on it.
The following example shows this - there is a function setAddress
which takes a new address, you can pass in the deployed FirstDeployedContract address.
Note: there is no check that _addr
really is a FirstDeployedContract
. It just trusts you.
// not shown here, but make sure FirstDeployedContract was imported in.
contract SecondContract {
FirstDeployedContract deployedContract;
function setAddress(address _addr) public {
deployedContract = FirstDeployedContract(_addr);
}
function setCounter(uint _val) public {
// assumes you called setAddress() with a valid FirstDeployedContract address
deployedContract.setCounter(_val); // << calls setCounter on the _addr
}
}
Calling a function in another contract by using its keccak256 signature
Maybe you don’t have the full contract details (or ABI). Maybe you just know there is a function called setCounter(uint)
on a smart contract deployed at an address.
A function is called by calculating the start of a keccak256 hash. There is a mapping in smart contracts that maps from that 4 bytes of keccak256 hash to a function call.
// ...
// 'deployedContract' is set up the same as previous code above.
function setCounterWithSignature(uint _newCounterVal) public {
deployedContract.call(bytes4(keccak256("setA(uint256)")),_newCounterVal);
}
// ...
Calling another contract, using assembly code (Yul)
As well as calling other contracts in normal Solidity code, it is of course also possible to call another contract using assembly code in Solidity (Yul)
You have to get the 4 bytes function selector, then call it with staticcall()
.
You can also use call()
but only for non view/pure functions.
You pass in how much gas to use on the call. If we just pass in gas()
, then all remaining gas possible will be sent as the max amount.
assembly {
mstore(0x00, theFunctionSelector); // theFunctionSelector should be 32 bytes here)
// 32-28 = 4 bytes.
// the selector will be the final 4 bytes (of the full 32 bytes)
let wasSuccessful := staticcall(gas(), theOtherContractAddress, 28, 32, 0x00, 0x20)
if(iszero(wasSuccessful) {
revert(0, 0)
}
// success!
}
This post is incomplete and a work-in-progress
I'll update it soon and flesh it out with
more
info!
Spotted a typo or have a suggestion to make this crypto dev article better? Please let me know!
📙 Solidity Auditing online quiz
Learn how to audit smart contracts by looking at some example code and trying to find the bugs
⛽ Solidity Gas Optimizations Guide
How to optimize and reduce gas usage in your smart contracts in Solidity
🧪 Guide to testing with Foundry
Guide to adding testing for your Solidity contracts, using the Foundry and Forge tools
📌 Guide to UTXO
UTXO and the UTXO set (used by blockchains such as Bitcoin) explained
📐 Solidity Assembly Guide
Introduction guide to using assembly in your Solidity smart contracts
📦 Ethereum EOF format explained
Information explaining what the upcoming Ethereum EOF format is all about