Who called the caller?
0xAE 0x00
CALLSTACKDEPTH
How many calls deep the current code is running.
0xAE 0x01
CALLERN
The caller of any frame further up the call stack.
MIP-18 is in Review and is not active on any network. It adds two opcodes under MIP-7's 0xAE prefix. The spec says they are for future Monad protocol upgrades and does not name them yet.
The information on this page should not be quoted. Please refer to MIP-18 for the authoritative spec.
Step through a call stack
Click a frame to run code there, then change n. CALLERN(n) returns what CALLER would return n frames up.
alice sends a transaction to an aggregator. It calls a router, the router calls a pool, and the pool calls a token.
Call frames
alice · sends the transaction
Click a frame to make it the one running.
CALLSTACKDEPTH
3
CALLERN(n)
Router
CALLER in F2
Calls to precompiles also add a frame and count toward depth. No code runs in that frame to read it.
The two opcodes
Both use the first two selectors after 0xAE. Neither takes argument bytes, so the encoding rules for extension arguments don't apply. How 0xAE works (MIP-7)
| Bytes | Name | Pops | Pushes | Gas |
|---|---|---|---|---|
| 0xAE 0x00 | CALLSTACKDEPTH | nothing | depth | 2 |
| 0xAE 0x01 | CALLERN | n | caller | 2 |
CALLSTACKDEPTH
Pushes the current depth. The transaction's own frame is depth 0.
PUSH1 1 CALLERN
Pushes the caller of the frame that called you. n comes from the stack, so it can be computed at runtime.
CALLSTACKDEPTH CALLERN
Uses the depth as n. The result is always the same as ORIGIN.
2 gas covers the whole two-byte opcode, the same as CALLER and ADDRESS. The client already holds the call stack in memory, so both run in constant time.
Don't use CALLERN for access control
A treasury lets only the admin withdraw. Compare checking CALLER with checking CALLERN(1).
The attacker gets the admin to call ClaimAirdrop. That contract calls Treasury.withdraw() and sends the funds to the attacker.
require(CALLER == admin)
CALLER = ClaimAirdrop
reverts
CALLER is ClaimAirdrop. The check fails and the funds stay put.
require(CALLERN(1) == admin)
CALLERN(1) = admin
passes
The admin is one frame up, so the check passes. The attacker's contract withdraws the funds.
This is the same problem as checking tx.origin. The spec says contracts should not use CALLERN(n) with n above 0 for authorization.
Who this affects
Contract developers
CALLER and ORIGIN, plus every frame between
CALLERN(0) is CALLER and CALLERN(depth) is ORIGIN. The callers in between could not be read from inside a contract before.
Auditors
Flag CALLERN in auth checks
A check on CALLERN(1) or higher can be passed by any contract the privileged account is tricked into calling.
Simulators, AA bundlers, analyzers
Behavior can depend on the path
A contract can act differently depending on how deep it is or who sits higher in the stack. Tools that assume only the direct caller, calldata and state matter may need changes.
The spec gives no use case beyond future Monad upgrades in development at Category Labs. Until those are published, the page can show what the opcodes return, but not which feature needs them.
Continue the discussion on Monad Forum
Questions, feedback, or a better idea? Weigh in on the forum thread.
Open forum thread