Skip to content
LogoLogo

Calling other contracts

A call that changes another contract's state is deferred with defer. It runs after this entrypoint's writes and events. Use with value = ... to attach ether.

calling-contracts.lithe
(* A state-changing call to another contract is deferred: it runs after this
   entrypoint's writes and events, never in the middle of them. *)
 
import "std/evm";
 
interface Token is {
  view function balanceOf(owner : address) : uint;
  function transfer(to : address; amount : uint) : bool;
}
 
interface Pool is {
  payable function deposit(beneficiary : address);
}
 
error Empty;
 
storage is record {
  token : Token;
  pool : Pool;
  swept : uint;
}
 
external function init(const token : Token; const pool : Pool) is {
  storage.token := token;
  storage.pool := pool;
}
 
external function sweep(const to : address)
  raises Empty
is {
  const held : uint := query storage.token.balanceOf(Evm.self());
  assert(held > 0, Empty);
  storage.swept := storage.swept + held;
  defer storage.token.transfer(to, held);
}
 
(* Attach ether with `with value = ...`. The value is a `uint` of wei, and only a
   method the interface declares `payable function` takes it. *)
external payable function forward() is {
  defer storage.pool.deposit(Evm.sender()) with value = uint(Evm.value());
}
  • The arguments are evaluated at the defer, so held is the balance read at that point.
  • deposit is declared payable function because Pool accepts ether with it. with value on a method that isn't payable, such as transfer, is a compile error.
  • The compiler warns that sweep and forward could be re-entered while their calls run. Add nonreentrant where that matters.