An example project for writing Arbitrum Stylus contracts in Rust using the stylus-sdk. It includes a Rust implementation of a vending machine Ethereum smart contract.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
// Rule 2: The vending machine's rules can't be changed by anyone.
contract VendingMachine {
// state variables = internal memory of the vending machine
mapping(address => uint) private _cupcakeBalances;
mapping(address => uint) private _cupcakeDistributionTimes;
function giveCupcakeTo(address userAddress) public returns (bool) {
// this code is unnecessary, but we're keeping it here so you can compare it to the JS implementation
if (_cupcakeDistributionTimes[userAddress] == 0) {
_cupcakeBalances[userAddress] = 0;
_cupcakeDistributionTimes[userAddress] = 0;
}
// Rule 1: The vending machine will distribute a cupcake to anyone who hasn't recently received one.
uint fiveSecondsFromLastDistribution = _cupcakeDistributionTimes[userAddress] + 5 seconds;
bool userCanReceiveCupcake = fiveSecondsFromLastDistribution <= block.timestamp;
if (userCanReceiveCupcake) {
_cupcakeBalances[userAddress]++;
_cupcakeDistributionTimes[userAddress] = block.timestamp;
return true;
} else {
revert("HTTP 429: Too Many Cupcakes (you must wait at least 5 seconds between cupcakes)");
}
}
// Getter function for the cupcake balance of a user
function getCupcakeBalanceFor(address userAddress) public view returns (uint) {
return _cupcakeBalances[userAddress];
}
}
You can export the Solidity ABI for your contract by using the cargo stylus
tool as follows:
cargo stylus export-abi
which outputs:
/**
* This file was automatically generated by Stylus and represents a Rust contract.
* For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs).
*/
interface VendingMachine {
function giveCupcakeTo(address user_address) external returns (bool);
function getCupcakeBalanceFor(address user_address) external view returns (uint256);
}
You can use the cargo stylus
command to deploy your contract to the Stylus-enabled Arbitrum Sepolia testnet. First, we can use the tool to check if our contract compiles to valid WASM for Stylus and verify that the deployment will succeed without submitting a transaction. By default, this will use the Arbitrum Sepolia testnet public RPC endpoint. See here for more information.
cargo stylus check
Next, we deploy:
cargo stylus deploy \
--private-key-path=<PRIVKEY_FILE_PATH>
This example includes how to call and transact with your contract in Rust using ethers-rs under examples/vending_machine.rs
. Your contracts are also Ethereum ABI equivalent if using the Stylus SDK, meaning they can be called and transacted with using any other Ethereum tooling.
By using the contract address from your deployment step above and your wallet, you can attempt to call the vending machine contract and interact with it.
To run the example, set the following env vars or place them in a .env
file this project, then:
STYLUS_CONTRACT_ADDRESS=<the onchain address of your deployed contract>
PRIV_KEY_PATH=<the file path for your priv key to transact with>
RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
USER_ADDRESS=<the address of the user you want to interact with>
Alternatively, you can copy the .env-sample
into a .env
file:
cp .env-sample .env
Next, run:
cargo run --example vending_machine --target=<YOUR_ARCHITECTURE>
Where you can find YOUR_ARCHITECTURE
by running rustc -vV | grep host
. For M1 Apple computers, for example, this is aarch64-apple-darwin
and for most Linux x86 it is x86_64-unknown-linux-gnu
.