Track 4 · Solidity foundations · lesson 14
Interfaces and external calls
11 min
Everything so far has happened inside one contract. This lesson is where your code starts talking to code you did not write — and where the danger begins.
An interface is a shape, not a contract
interface ICounter {
function increment() external;
function count() external view returns (uint256);
}
No bodies, no state, no constructor. It tells the compiler how to encode a call to something at an address that claims to behave this way.
ICounter(someAddress) does not check anything. There is no verification that
the address holds a contract, that the contract has those functions, or that
they do what the names suggest.
You are asserting a shape. If you are wrong, the call reverts — or worse, silently hits a different function whose selector happens to match.
Nudge
Show me the approach
Show me the code
Explain the solution
What an external call really is
Each counter.increment() is a whole separate execution:
- A new execution context, with its own storage and memory.
msg.senderbecomes your contract's address, not the original user.- Control leaves your contract entirely until the call returns.
That third point is the important one.
Predict
Your contract sends ETH to an address, then updates its records. The recipient is a contract. What can it do while it has control?
Assume every external call is hostile
Three habits worth forming now:
- Update your state before calling out. Always.
- Check return values. A low-level
.call()returnsfalserather than reverting. - Assume the callee can call you back, run out of gas, or revert on purpose to block you.
None of this is paranoia. Every one of these has been used to take real money.
Check
Contract A calls contract B. Inside B, what is `msg.sender`?
Worth remembering
- An interface declares a shape; casting an address to it verifies nothing.
- An external call is a separate execution context, and `msg.sender` becomes the calling contract.
- Control genuinely leaves your contract — the callee can call you back before you finish.
- Checks-effects-interactions: validate, update state, then call out.
- Low-level `.call()` returns false instead of reverting; always check it.