Contract Interactions
Our Contracts Are No Longer Sandboxed!
While we are beginning to write more sophisticated smart contracts, the one thing that holds back our contracts is the inability to interact with other smart contracts. Almost every popular contract on EVM-compatible chains contain logic which allows it to call the functions of other smart contracts.
To understand how contract interactions might look like, consider the following examples:
Uniswap: Assume you place a trade which swaps USDC for WETH on app.uniswap.org. Your trade is first sent to the Uniswap V3 SwapRouter.sol which then calls the relevant USDC-WETH pool contract necessary to conduct the desired swap.
MultiSig Wallet: All multi-signature wallets (multisigs) on Ethereum and similar chains are smart contracts. Therefore, contract-to-contract interactions are an inherent feature of multisigs.
Primer: Address Type Methods
Before discussing the logistics on contract interactions, we will first discuss the types that allow us to call other contracts in the first place - the address type and contract types.
The address type, as previously discussed in Primitive Values & Types, represents the addresses of accounts. What is unique about the address type is that it comes with built-in methods that allows us to interact with values of the address type.
To motivate our understanding, consider the following variable:
As the name might suggest, this is the address of Vitalik Buterin's Ethereum account. To get an idea of the functions that the address type provide, we can, as an example, get the balance of Vitalik's account.
At the time of writing this, vitalik.balance
returns 4119625876629850684131
, which is the amount of Wei in his account.
Contract Types and Calling Other Contracts
The address type provides many functions that we will soon see, but with our current understanding of Solidity, it is useful mainly when dealing with EOAs since there is no nice way to call functions of contracts under the address type. Thankfully, Solidity provides us with contract types which allow us to treat contracts like their own types. To understand what we mean by contract types, consider the following code:
Furthermore, let's assume that address that contract A
is deployed at is as follows:
Our goal is to be able to call the getNum
function of the contract located at contractAccount
. Ideally, we want to be able to call getNum
directly; using contract types, we first convert contractAccount
to type A
:
Having now defined convertedContract
, we can now call getNum
as follows:
Since contract types are types, we can use them wherever regular types are also used. An example of this can be found below:
Sending Ether
While we are now able to call the functions of other smart contracts, there is still one question remaining: how can we send ether to other contracts?
It is trivially known that users can send ether to each other. Since contracts are also accounts, we can send ether to and from contracts as well. In this last section, we will cover how to write code that allows our contracts to send ether to other Ethereum accounts and how to specify our functions to be able to receive ether.
The call
Function
call
FunctionAssume we want to send x
ether to an account (EOA or contract) with address y
. Assuming that the contract we are sending from has enough ether, the syntax below demonstrates how we can send ether via smart contracts:
The call
function is available to all values of type address. Although we will see later in the course that call
is a powerful function, for our current purposes, we will treat call
as a function that allows us to send ether to any account.
There are two types of parameters that are used in the call
function:
Special Parameters: consists of the
value
field and thegas
field.value
is the amount of wei we are sending to the account, whilegas
specifies the maximum amount of gas that can be used when calling the account.Data Parameter: consists of a hexadecimal value to be sent when calling the account
Note that special parameters go inside curly brackets and use colon-notation, while the data parameter goes in rounded brackets and uses equality-notation.
The call
function, furthermore, returns two values; these values are as follows:
where success
indicates where the call function was successful (i.e. whether the transfer of ether was successful) and data
is the data returned from the call
function.
The Payable
Keyword
Payable
KeywordNow that we know how to program our smart contracts to send ether to other accounts (EOAs or contracts), its natural to ask whether if we can include ether as part of function calls. To motivate our curiousity, consider the following example:
The contract above represents a club where anyone can obtain membership if they pay the require 1 wei membership fee. Examining our contract line-by-line, we have the following:
Line 8: we are checking if the user has sent the required 1 wei with the function call
Line 9: we are setting the user's membership status to true
Line 10: we are return
true
(indicating to the caller that the membership was successfully obtrained)
To send ether alongside a contract call, we again can attach special parameters like we did for the call
function; the syntax for attaching special parameters in the case of the becomeMember
function is as follows:
Tying this all together, the following code example consists of the Club
contract and a User
contract which applies for club membership:
If you try to compile this code, however, you will arrive at an angry compiler telling you that you cannot send value to a "nonpayable" function. What we are missing is to specify that becomeMember
can receive ether. To do this, we need to attach the payable
keyword to the function header of becomeMember
. The following code includes the payable
keyword:
Preview: Responding to Ether Received
In the case that we send ether alongside a function call, the logic of the function involved will be triggered. However, what if we send ether to a contract without calling a specific function (i.e. sending ether via the call
function)? Intuitively, one would guess that nothing happens, just like when we send ether to an EOA. However, as it turns out, contracts can be programmed to respond when they receive ether. Via default functions, developers can dictate what a contract does when it receives ether.
Last updated