3. Testing External Interaction
A smart contract in EVM can interact with each other by making external calls to another contract (a contract can technically make an external call to itself) to update their states or fetch some data.
Smart contracts that have functionality that depends on external contracts can have unforeseen risks that they cannot control. If the address of the external contract is not a trusted address, the owner of the external contract can manipulate the function that the other contracts rely on to gain benefit from the affected contracts.
3.1. Invoking external calls
There are three types of call instructions in EVM: STATICCALL
, CALL
, and DELEGATECALL
. For an internal function call, EVM uses JUMP
instruction instead of the mentioned call instruction. The STATICCALL
instruction is a call that does not change the states of blockchains. It can be used to call a function that doesn't change states, e.g., functions with view
and pure
visibility. Unlike the CALL
and DELEGATECALL
instructions, they can be used to call a contract and affect states on the blockchain. But they have a distinct use case.
The CALL
instruction has the targeted address contract execute the function and return the value back to the caller.
Similarly, the DELEGATECALL
instruction executes the function at the targeted address but with the states of the caller, which include the value of the msg.sender
state. Since it executes with targeted contract implementation but the states of the caller are affected, using the DELEGATECALL
instruction to call an untrusted contract could cause serious effects to the caller's states. The target of the DELEGATECALL
instruction must always be controlled thoroughly.
Testing
3.1.1. Unknown external components should not be invoked
Check that only known and trusted contracts are invoked. Please have a look at the following example vulnerable contract.
In the contract above, the router
can be set to any contract, allowing the attacker to implement a malicious contract that returns a high balance without actually swapping. Therefore, the smart contract should perform external calls to only known and trusted smart contracts, or define a whitelist of trustable contracts.
3.1.2. Delegatecall should not be used on untrusted contracts
Check that delegatecall
is only used on trusted contracts.
In the example contract above, the delegatecall
is used on a user supplied address, worker, allowing the attacker to create a contract with the work()
function and perform arbitrary actions on the contract, such as assigning slot 0 with the new address as the owner
.
3.1.3. Invoke function with “this” keyword should be used with caution
The this
keyword in Solidity is used to retrieve the properties of the current smart contract address. When using this
to invoke a function (this.'functionName') in the smart contract, it means the contract making an external call to the function to itself, which change the msg.sender from the former sender into the the contract itself.
The test can be done by checking for the use of this.'functionName' statement that affects the logic of the use of msg.sender
, and make sure that it is correct according to the business design.
The offer()
function allows users to offer NFTs for sale through the buy()
function, and the bulkOffer()
function allows users to offer multiple NFTs for sale in a single transaction. The owner of the offered NFTs will be msg.sender
. However, because the bulkOffer()
function applies this.offer()
, the caller will be the contract address rather than an EOA. This means that an attacker could steal NFTs from the contract by calling the bulkOffer()
function with existing NFTs in the contract, along with a worthless token, and then executing the buy()
function to acquire those NFTs.
Checklist
Unknown external components should not be invoked
delegatecall
should not be used on untrusted contractsCalling itself is counted as making an external call to itself, so the invariants should be adjusted to fulfill
Last updated