Arbitrum Stylus logo

Stylus by Example

Sending Ether

We have three main ways to send Ether in Rust Stylus:

  • transfer_eth
  • Low-level call
  • Sending value while calling an external contract via an interface (Call::new_payable)

Important: transfer_eth in Stylus invokes the recipient contract and forwards execution with your full call gas (the callee can spend it and even make further calls). In Solidity, transfer historically forwarded only 2,300 gas. In Stylus, if you need to cap gas, use the low-level call and set a gas limit on the Call context.

These two are equivalent under the hood:

1transfer_eth(self.vm(), recipient, value)?;
2call(self.vm(), Call::new_payable(self, value), recipient, &[])?;
1transfer_eth(self.vm(), recipient, value)?;
2call(self.vm(), Call::new_payable(self, value), recipient, &[])?;

Where to Send Ether

  • EOA addresses: Send Ether directly.
  • Solidity contracts with receive() (no calldata): Send Ether without calldata.
  • Solidity contracts with fallback() (with calldata): Send Ether with calldata.
  • Contracts with payable methods (Solidity or Stylus): Call a specific payable function (e.g., receiveEther()), passing value via Call::new_payable.

Reentrancy & gas tips (quick notes)

  • Reentrancy: Any Ether transfer that executes recipient code (all of the above do) can reenter. Follow checks-effects-interactions and/or implement guards in your calling logic.
  • Gas limiting: Use Call::new_payable(self, value).gas(limit) when you must cap recipient gas.
  • EOA vs Contract: EOAs ignore gas limits (no code), but contracts will execute receive()/fallback()/payable handlers.

Below you can find examples for each of these methods and how to define them in a Rust Stylus smart contract using the Stylus SDK:

Example Code:

src/lib.rs

1Loading...
1Loading...

Cargo.toml

1Loading...
1Loading...

src/main.rs

1Loading...
1Loading...