@@ -34,8 +34,8 @@ pub struct HelloWorld;
3434
3535#[public]
3636impl HelloWorld {
37- fn user_main ( _input : Vec < u8 > ) -> ArbResult {
38- Ok ( Vec :: new () )
37+ pub fn greet ( & self ) -> String {
38+ " Hello, Stylus! " . to_string ( )
3939 }
4040}
4141```
@@ -44,8 +44,8 @@ This contract:
4444
4545- Uses ` #[storage] ` to define the contract struct (empty in this case)
4646- Uses ` #[entrypoint] ` to mark it as the contract's entry point
47- - Uses ` #[public] ` to expose the ` user_main ` function
48- - Returns ` ArbResult ` , which is ` Result<Vec<u8>, Vec<u8>> `
47+ - Uses ` #[public] ` to expose the ` greet ` method
48+ - Exposes a single read-only method callable from Solidity or other contracts
4949
5050## Storage definition
5151
@@ -101,7 +101,7 @@ The `#[entrypoint]` macro marks a struct as the contract's main entry point. It
101101
102102- Routing incoming calls to public methods
103103- Managing contract storage
104- - Handling reentrancy protection (unless the ` reentrant ` feature is enabled)
104+ - Flushing the storage cache automatically on cross-contract calls, which protects against reentrancy by default; follow the checks-effects-interactions pattern for additional safety
105105
106106** Key requirements:**
107107
@@ -187,7 +187,8 @@ impl MyContract {
187187 let sender = self . vm (). msg_sender ();
188188 let balance = self . balances. get (sender );
189189 self . balances. setter (sender ). set (balance - amount );
190- self . balances. setter (to ). set (self . balances. get (to ) + amount );
190+ let to_balance = self . balances. get (to );
191+ self . balances. setter (to ). set (to_balance + amount );
191192 }
192193
193194 // Pure: no state access at all
@@ -219,7 +220,7 @@ sol_storage! {
219220impl Token {
220221 #[constructor]
221222 pub fn constructor (& mut self , initial_supply : U256 ) {
222- let deployer = self . vm (). msg_sender ();
223+ let deployer = self . vm (). tx_origin ();
223224 self . owner. set (deployer );
224225 self . total_supply. set (initial_supply );
225226 }
@@ -242,7 +243,7 @@ impl Token {
242243 pub fn constructor (& mut self , initial_supply : U256 ) {
243244 // Contract can receive ETH during deployment
244245 let received = self . vm (). msg_value ();
245- self . owner. set (self . vm (). msg_sender ());
246+ self . owner. set (self . vm (). tx_origin ());
246247 self . total_supply. set (initial_supply );
247248 }
248249}
@@ -562,7 +563,8 @@ impl Token {
562563 return false ;
563564 }
564565 self . balances. setter (from ). set (from_balance - value );
565- self . balances. setter (to ). set (self . balances. get (to ) + value );
566+ let to_balance = self . balances. get (to );
567+ self . balances. setter (to ). set (to_balance + value );
566568
567569 // Emit event
568570 self . vm (). log (Transfer { from , to , value });
@@ -625,7 +627,7 @@ sol_interface! {
625627#### View calls (read-only)
626628
627629``` rust
628- use stylus_sdk :: call :: Call ;
630+ use stylus_sdk :: prelude :: * ; // brings `Call` into scope
629631
630632#[public]
631633impl MyContract {
@@ -687,6 +689,7 @@ For maximum flexibility, use raw calls:
687689
688690``` rust
689691use stylus_sdk :: call :: {call, static_call, RawCall };
692+ use stylus_sdk :: prelude :: * ; // brings `Call` into scope
690693
691694#[public]
692695impl MyContract {
@@ -695,12 +698,12 @@ impl MyContract {
695698 let config = Call :: new_mutating (self )
696699 . gas (self . vm (). evm_gas_left ());
697700
698- call (self . vm (), config , target , & calldata )
701+ Ok ( call (self . vm (), config , target , & calldata ) ? )
699702 }
700703
701704 // Static call (read-only)
702705 pub fn execute_static_call (& self , target : Address , calldata : Vec <u8 >) -> Result <Vec <u8 >, Vec <u8 >> {
703- static_call (self . vm (), Call :: new (), target , & calldata )
706+ Ok ( static_call (self . vm (), Call :: new (), target , & calldata ) ? )
704707 }
705708
706709 // Unsafe raw call with advanced options
@@ -767,7 +770,8 @@ impl Token {
767770 }
768771
769772 self . balances. setter (from ). set (balance - amount );
770- self . balances. setter (to ). set (self . balances. get (to ) + amount );
773+ let to_balance = self . balances. get (to );
774+ self . balances. setter (to ). set (to_balance + amount );
771775
772776 Ok (true )
773777 }
@@ -829,7 +833,7 @@ impl SimpleToken {
829833 // Constructor
830834 #[constructor]
831835 pub fn constructor (& mut self , initial_supply : U256 ) {
832- let deployer = self . vm (). msg_sender ();
836+ let deployer = self . vm (). tx_origin ();
833837 self . owner. set (deployer );
834838 self . balances. setter (deployer ). set (initial_supply );
835839 self . total_supply. set (initial_supply );
@@ -906,7 +910,8 @@ impl SimpleToken {
906910 return Err (TokenError :: Unauthorized (Unauthorized {}));
907911 }
908912
909- self . balances. setter (to ). set (self . balances. get (to ) + value );
913+ let to_balance = self . balances. get (to );
914+ self . balances. setter (to ). set (to_balance + value );
910915 self . total_supply. set (self . total_supply. get () + value );
911916
912917 self . vm (). log (Transfer {
@@ -930,7 +935,8 @@ impl SimpleToken {
930935 }
931936
932937 self . balances. setter (from ). set (from_balance - value );
933- self . balances. setter (to ). set (self . balances. get (to ) + value );
938+ let to_balance = self . balances. get (to );
939+ self . balances. setter (to ). set (to_balance + value );
934940
935941 self . vm (). log (Transfer { from , to , value });
936942 Ok (())
@@ -1074,7 +1080,7 @@ pub fn withdraw(&mut self, amount: U256) -> Result<(), VaultError> {
10741080 self . balances. setter (caller ). set (balance - amount );
10751081
10761082 // Interactions (external calls last)
1077- // self.transfer_eth( caller, amount)?;
1083+ // transfer_eth( self.vm(), caller, amount)?;
10781084
10791085 Ok (())
10801086}
@@ -1115,7 +1121,8 @@ This pattern is essential for building upgradeable contracts, proxy patterns, an
11151121The ` delegate_call ` function is a low-level operation similar to ` call ` and ` static_call ` . It is considered unsafe because it relies on an external contract to ensure safety.
11161122
11171123``` rust
1118- pub unsafe fn delegate_call (
1124+ pub unsafe fn delegate_call <H : Host + ? Sized >(
1125+ host : & H ,
11191126 context : impl MutatingCallContext ,
11201127 to : Address ,
11211128 data : & [u8 ],
@@ -1126,14 +1133,16 @@ pub unsafe fn delegate_call(
11261133
11271134```rust
11281135use stylus_sdk :: call :: delegate_call ;
1136+ use stylus_sdk :: prelude :: * ; // brings `Call` into scope
11291137
11301138pub fn low_level_delegate_call (
11311139 & mut self ,
11321140 calldata : Vec <u8 >,
11331141 target : Address ,
11341142) -> Result <Vec <u8 >, DelegateCallErrors > {
11351143 unsafe {
1136- let result = delegate_call (self , target , & calldata )
1144+ let config = Call :: new_mutating (self );
1145+ let result = delegate_call (self . vm (), config , target , & calldata )
11371146 . map_err (| _ | DelegateCallErrors :: DelegateCallFailed (DelegateCallFailed {}))? ;
11381147 Ok (result )
11391148 }
@@ -1152,20 +1161,24 @@ pub fn raw_delegate_call(
11521161 calldata : Vec <u8 >,
11531162 target : Address ,
11541163) -> Result <Vec <u8 >, Vec <u8 >> {
1155- let data = RawCall :: new_delegate () // Configure a delegate call
1156- . gas (2100 ) // Supply 2100 gas
1157- . limit_return_data (0 , 32 ) // Only read the first 32 bytes back
1158- . call (target , & calldata )? ;
1164+ let data = unsafe {
1165+ RawCall :: new_delegate (self . vm ()) // Configure a delegate call
1166+ . gas (2100 ) // Supply 2100 gas
1167+ . limit_return_data (0 , 32 ) // Only read the first 32 bytes back
1168+ . call (target , & calldata )?
1169+ };
11591170
11601171 Ok (data )
11611172}
11621173```
11631174
11641175### Safety considerations
11651176
1166- :::caution
1177+ <VanillaAdmonition type = " warning" >
1178+
11671179Delegate calls are inherently unsafe and should be used with caution.
1168- :::
1180+
1181+ </VanillaAdmonition >
11691182
11701183- ** Trust requirement** : The calling contract must trust the external contract to uphold safety requirements
11711184- ** Storage modification** : The external contract can arbitrarily change the calling contract's storage
@@ -1207,7 +1220,8 @@ impl DelegateExample {
12071220 target : Address ,
12081221 ) -> Result <Vec <u8 >, DelegateCallErrors > {
12091222 unsafe {
1210- let result = delegate_call (self , target , & calldata )
1223+ let config = Call :: new_mutating (self );
1224+ let result = delegate_call (self . vm (), config , target , & calldata )
12111225 . map_err (| _ | DelegateCallErrors :: DelegateCallFailed (DelegateCallFailed {}))? ;
12121226
12131227 Ok (result )
@@ -1220,10 +1234,12 @@ impl DelegateExample {
12201234 calldata : Vec <u8 >,
12211235 target : Address ,
12221236 ) -> Result <Vec <u8 >, Vec <u8 >> {
1223- let data = RawCall :: new_delegate ()
1224- . gas (2100 )
1225- . limit_return_data (0 , 32 )
1226- . call (target , & calldata )? ;
1237+ let data = unsafe {
1238+ RawCall :: new_delegate (self . vm ())
1239+ . gas (2100 )
1240+ . limit_return_data (0 , 32 )
1241+ . call (target , & calldata )?
1242+ };
12271243
12281244 Ok (data )
12291245 }
@@ -1247,7 +1263,7 @@ Stylus provides multiple ways to send ether from a contract. Unlike Solidity's `
12471263The simplest way to send ether:
12481264
12491265``` rust
1250- use stylus_sdk :: call :: transfer_eth;
1266+ use stylus_sdk :: call :: transfer :: transfer_eth;
12511267
12521268#[public]
12531269impl SendEther {
@@ -1264,13 +1280,16 @@ impl SendEther {
12641280For more control over the transfer:
12651281
12661282``` rust
1267- use stylus_sdk :: call :: {call, Call };
1283+ use stylus_sdk :: call :: call;
1284+ use stylus_sdk :: prelude :: * ; // brings `Call` into scope
12681285
12691286#[public]
12701287impl SendEther {
12711288 #[payable]
12721289 pub fn send_via_call (& mut self , to : Address ) -> Result <(), Vec <u8 >> {
1273- call (Call :: new_in (self ). value (self . vm (). msg_value ()), to , & [])? ;
1290+ let value = self . vm (). msg_value ();
1291+ let context = Call :: new_payable (self , value );
1292+ call (self . vm (), context , to , & [])? ;
12741293 Ok (())
12751294 }
12761295}
@@ -1281,7 +1300,8 @@ These two approaches are equivalent under the hood:
12811300``` rust
12821301// These are equivalent:
12831302transfer_eth (self . vm (), recipient , value )? ;
1284- call (Call :: new_in (self ). value (value ), recipient , & [])? ;
1303+ let context = Call :: new_payable (self , value );
1304+ call (self . vm (), context , recipient , & [])? ;
12851305```
12861306
12871307### Sending with a gas limit
@@ -1291,11 +1311,9 @@ To cap the gas forwarded to the recipient (similar to Solidity's `transfer`):
12911311``` rust
12921312#[payable]
12931313pub fn send_via_call_gas_limit (& mut self , to : Address , gas_amount : u64 ) -> Result <(), Vec <u8 >> {
1294- call (
1295- Call :: new_in (self ). value (self . vm (). msg_value ()). gas (gas_amount ),
1296- to ,
1297- & [],
1298- )? ;
1314+ let value = self . vm (). msg_value ();
1315+ let context = Call :: new_payable (self , value ). gas (gas_amount );
1316+ call (self . vm (), context , to , & [])? ;
12991317 Ok (())
13001318}
13011319```
@@ -1313,7 +1331,9 @@ pub fn send_via_call_with_calldata(
13131331 to : Address ,
13141332 data : Bytes ,
13151333) -> Result <(), Vec <u8 >> {
1316- call (Call :: new_in (self ). value (self . vm (). msg_value ()), to , data . as_slice ())? ;
1334+ let value = self . vm (). msg_value ();
1335+ let context = Call :: new_payable (self , value );
1336+ call (self . vm (), context , to , & data )? ;
13171337 Ok (())
13181338}
13191339```
@@ -1334,8 +1354,9 @@ impl SendEther {
13341354 #[payable]
13351355 pub fn send_to_contract (& mut self , to : Address ) -> Result <(), Vec <u8 >> {
13361356 let target = ITarget :: new (to );
1337- let config = Call :: new_in (self ). value (self . vm (). msg_value ());
1338- target . receive_ether (config )? ;
1357+ let value = self . vm (). msg_value ();
1358+ let context = Call :: new_payable (self , value );
1359+ target . receive_ether (self . vm (), context )? ;
13391360 Ok (())
13401361 }
13411362}
@@ -1357,7 +1378,7 @@ extern crate alloc;
13571378use alloy_primitives :: Address ;
13581379use stylus_sdk :: {
13591380 abi :: Bytes ,
1360- call :: {call, transfer_eth, Call },
1381+ call :: {call, transfer :: transfer_eth},
13611382 prelude :: * ,
13621383};
13631384
@@ -1375,26 +1396,27 @@ pub struct SendEther;
13751396impl SendEther {
13761397 // Simple transfer
13771398 #[payable]
1378- pub fn send_via_transfer (& self , to : Address ) -> Result <(), Vec <u8 >> {
1379- transfer_eth (self . vm (), to , self . vm (). msg_value ())? ;
1399+ pub fn send_via_transfer (& mut self , to : Address ) -> Result <(), Vec <u8 >> {
1400+ let value = self . vm (). msg_value ();
1401+ transfer_eth (self . vm (), to , value )? ;
13801402 Ok (())
13811403 }
13821404
13831405 // Low-level call
13841406 #[payable]
13851407 pub fn send_via_call (& mut self , to : Address ) -> Result <(), Vec <u8 >> {
1386- call (Call :: new_in (self ). value (self . vm (). msg_value ()), to , & [])? ;
1408+ let value = self . vm (). msg_value ();
1409+ let context = Call :: new_payable (self , value );
1410+ call (self . vm (), context , to , & [])? ;
13871411 Ok (())
13881412 }
13891413
13901414 // With gas limit
13911415 #[payable]
13921416 pub fn send_via_call_gas_limit (& mut self , to : Address , gas_amount : u64 ) -> Result <(), Vec <u8 >> {
1393- call (
1394- Call :: new_in (self ). value (self . vm (). msg_value ()). gas (gas_amount ),
1395- to ,
1396- & [],
1397- )? ;
1417+ let value = self . vm (). msg_value ();
1418+ let context = Call :: new_payable (self , value ). gas (gas_amount );
1419+ call (self . vm (), context , to , & [])? ;
13981420 Ok (())
13991421 }
14001422
@@ -1405,16 +1427,19 @@ impl SendEther {
14051427 to : Address ,
14061428 data : Bytes ,
14071429 ) -> Result <(), Vec <u8 >> {
1408- call (Call :: new_in (self ). value (self . vm (). msg_value ()), to , data . as_slice ())? ;
1430+ let value = self . vm (). msg_value ();
1431+ let context = Call :: new_payable (self , value );
1432+ call (self . vm (), context , to , & data )? ;
14091433 Ok (())
14101434 }
14111435
14121436 // To payable contract method
14131437 #[payable]
14141438 pub fn send_to_contract (& mut self , to : Address ) -> Result <(), Vec <u8 >> {
14151439 let target = ITarget :: new (to );
1416- let config = Call :: new_in (self ). value (self . vm (). msg_value ());
1417- target . receive_ether (config )? ;
1440+ let value = self . vm (). msg_value ();
1441+ let context = Call :: new_payable (self , value );
1442+ target . receive_ether (self . vm (), context )? ;
14181443 Ok (())
14191444 }
14201445}
@@ -1424,14 +1449,16 @@ impl SendEther {
14241449
14251450The factory pattern allows a contract to deploy other contracts programmatically. This is useful for creating contract instances on demand, such as deploying new token contracts or creating user-specific vaults.
14261451
1427- :::note
1452+ <VanillaAdmonition type = " note" >
1453+
14281454Advanced deployment patterns documentation is in development. This section will cover:
14291455
14301456- Deploying contracts from within a contract
14311457- Passing constructor arguments
14321458- Deterministic deployment with CREATE2
14331459- Handling deployment failures
1434- :::
1460+
1461+ </VanillaAdmonition >
14351462
14361463** Constructor considerations for factory-deployed contracts:**
14371464
0 commit comments