diff --git a/.github/codecov.yml b/.github/codecov.yml index 9359748ed..0bfaa55da 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -15,14 +15,11 @@ coverage: default: informational: true # Docs and examples are not relevant to coverage. -# Motsu and e2e should be moved outside of this repo. ignore: - "docs" - "examples" - "lib/e2e" - "lib/e2e-proc" - - "lib/motsu" - - "lib/motsu-proc" # Make comments less noisy. comment: layout: "files" diff --git a/.github/workflows/check-publish.yml b/.github/workflows/check-publish.yml index ae73301c7..86c734c50 100644 --- a/.github/workflows/check-publish.yml +++ b/.github/workflows/check-publish.yml @@ -4,9 +4,9 @@ permissions: contents: read on: push: - branches: [ main, release/* ] + branches: [main, release/*] pull_request: - branches: [ main, release/* ] + branches: [main, release/*] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -25,12 +25,6 @@ jobs: with: rustflags: "" - - name: Check motsu-proc - run: cargo publish -p motsu-proc --dry-run - - - name: Check motsu - run: cargo publish -p motsu --dry-run - - name: Check openzeppelin-crypto run: cargo publish -p openzeppelin-crypto --target wasm32-unknown-unknown --dry-run diff --git a/.github/workflows/publish-motsu-proc.yml b/.github/workflows/publish-motsu-proc.yml deleted file mode 100644 index 5e392138c..000000000 --- a/.github/workflows/publish-motsu-proc.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: publish motsu-proc -# This workflow publishes motsu-proc on crates.io. -permissions: - contents: read -on: - workflow_dispatch -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true -env: - CARGO_TERM_COLOR: always -jobs: - check-publish: - name: Publish motsu-proc on crates.io - env: - MOTSU_PROC_TOKEN: ${{ secrets.MOTSU_PROC_TOKEN }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - rustflags: "" - - - name: Check motsu-proc - run: cargo publish -p motsu-proc --dry-run - - - name: Publish motsu-proc - run: cargo publish -p motsu-proc --token $MOTSU_PROC_TOKEN diff --git a/.github/workflows/publish-motsu.yml b/.github/workflows/publish-motsu.yml deleted file mode 100644 index ec7b4e48e..000000000 --- a/.github/workflows/publish-motsu.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: publish motsu -# This workflow publishes motsu on crates.io. -permissions: - contents: read -on: - workflow_dispatch -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true -env: - CARGO_TERM_COLOR: always -jobs: - check-publish: - name: Publish motsu on crates.io - env: - MOTSU_TOKEN: ${{ secrets.MOTSU_TOKEN }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - rustflags: "" - - - name: Check motsu - run: cargo publish -p motsu --dry-run - - - name: Publish motsu - run: cargo publish -p motsu --token $MOTSU_TOKEN diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ab68dd2..abd9f1c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [v0.2.0-alpha.2] - 2024-12-18 ### Added @@ -27,10 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed (Breaking) - Update internal functions of `Erc721` and `Erc721Consecutive` to accept a reference to `Bytes`. #437 -- Stop supporting reentrancy, and borrow `self` immutably in `IErc721::_check_on_erc721_received`. #440 -- Remove `&mut self` parameter from `IErc1155::_check_on_erc1155_received` and make it an associated function. #440 -- Remove `storage: &mut impl TopLevelStorage` parameter from `ecdsa::recover`. #440 -- Remove `TopLevelStorage` trait implementation from `VestingWallet`, `Erc1155`, `Erc20Permit`, `SafeErc20`, `Erc721Consecutive`, and `Erc721`. #440 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index ff7be5413..19d6f3afa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "access-control-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -792,7 +792,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy-primitives", "openzeppelin-stylus", @@ -801,7 +801,7 @@ dependencies = [ [[package]] name = "basic-example-script" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -812,7 +812,7 @@ dependencies = [ [[package]] name = "benches" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1254,7 +1254,7 @@ dependencies = [ [[package]] name = "cryptography-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1550,7 +1550,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erc1155-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1563,7 +1563,7 @@ dependencies = [ [[package]] name = "erc1155-metadata-uri-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1576,7 +1576,7 @@ dependencies = [ [[package]] name = "erc1155-supply-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1589,7 +1589,7 @@ dependencies = [ [[package]] name = "erc20-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1602,7 +1602,7 @@ dependencies = [ [[package]] name = "erc20-permit-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1615,7 +1615,7 @@ dependencies = [ [[package]] name = "erc721-consecutive-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1630,7 +1630,7 @@ dependencies = [ [[package]] name = "erc721-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -1644,7 +1644,7 @@ dependencies = [ [[package]] name = "erc721-metadata-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -2419,7 +2419,7 @@ dependencies = [ [[package]] name = "merkle-proofs-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -2470,7 +2470,9 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "motsu" -version = "0.2.0" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc9f12a78b4ce861c6a73758b137200a7139b402bc3fd8baa0477e936a572605" dependencies = [ "const-hex", "dashmap 6.1.0", @@ -2482,14 +2484,12 @@ dependencies = [ [[package]] name = "motsu-proc" -version = "0.2.0" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e477b432a80b129a0ecb1da2ba257140e73655bd922f2fd02a7b13e76216374" dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "motsu", "proc-macro2", "quote", - "stylus-sdk", "syn 2.0.68", ] @@ -2630,7 +2630,7 @@ dependencies = [ [[package]] name = "openzeppelin-crypto" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "crypto-bigint", "educe", @@ -2644,7 +2644,7 @@ dependencies = [ [[package]] name = "openzeppelin-stylus" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -2670,7 +2670,7 @@ dependencies = [ [[package]] name = "ownable-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -2683,7 +2683,7 @@ dependencies = [ [[package]] name = "ownable-two-step" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -3297,7 +3297,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "safe-erc20-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", @@ -4063,7 +4063,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vesting-wallet-example" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 66270beb7..0357aeef7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,6 @@ members = [ "contracts", "contracts-proc", "lib/crypto", - "lib/motsu", - "lib/motsu-proc", "lib/e2e", "lib/e2e-proc", "examples/erc20", @@ -30,8 +28,6 @@ default-members = [ "contracts", "contracts-proc", "lib/crypto", - "lib/motsu", - "lib/motsu-proc", "lib/e2e-proc", "examples/erc20", "examples/erc20-permit", @@ -61,7 +57,7 @@ authors = ["OpenZeppelin"] edition = "2021" license = "MIT" repository = "https://github.com/OpenZeppelin/rust-contracts-stylus" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" [workspace.lints.rust] missing_docs = "warn" @@ -121,12 +117,13 @@ syn = { version = "2.0.58", features = ["full"] } proc-macro2 = "1.0.79" quote = "1.0.35" +# test helpers +motsu = "0.2.1" + # members openzeppelin-stylus = { path = "contracts" } openzeppelin-stylus-proc = { path = "contracts-proc", version = "0.1.0" } openzeppelin-crypto = { path = "lib/crypto" } -motsu = { path = "lib/motsu" } -motsu-proc = { path = "lib/motsu-proc", version = "0.2.0" } e2e = { path = "lib/e2e" } e2e-proc = { path = "lib/e2e-proc" } diff --git a/README.md b/README.md index 17300df0a..feb6807c3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [`koba`]: https://github.com/OpenZeppelin/koba -[Unit]: ./lib/motsu/README.md +[Unit]: https://github.com/OpenZeppelin/stylus-test-helpers [integration]: ./lib/e2e/README.md diff --git a/contracts/src/finance/vesting_wallet.rs b/contracts/src/finance/vesting_wallet.rs index 3cbd55c43..e065c7b76 100644 --- a/contracts/src/finance/vesting_wallet.rs +++ b/contracts/src/finance/vesting_wallet.rs @@ -33,7 +33,7 @@ use stylus_sdk::{ call::{self, call, Call}, contract, evm, function_selector, prelude::storage, - storage::{StorageMap, StorageU256, StorageU64}, + storage::{StorageMap, StorageU256, StorageU64, TopLevelStorage}, stylus_proc::{public, SolidityError}, }; @@ -118,6 +118,11 @@ pub struct VestingWallet { pub safe_erc20: SafeErc20, } +/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when +/// calling other contracts and not `&mut (impl TopLevelStorage + +/// BorrowMut)`. Should be fixed in the future by the Stylus team. +unsafe impl TopLevelStorage for VestingWallet {} + /// Required interface of a [`VestingWallet`] compliant contract. #[interface_id] pub trait IVestingWallet { @@ -421,7 +426,7 @@ impl IVestingWallet for VestingWallet { let owner = self.ownable.owner(); - call(Call::new().value(amount), owner, &[])?; + call(Call::new_in(self).value(amount), owner, &[])?; evm::log(EtherReleased { amount }); diff --git a/contracts/src/token/erc1155/extensions/supply.rs b/contracts/src/token/erc1155/extensions/supply.rs index 6efbbf935..80e9fd61f 100644 --- a/contracts/src/token/erc1155/extensions/supply.rs +++ b/contracts/src/token/erc1155/extensions/supply.rs @@ -279,7 +279,7 @@ impl Erc1155Supply { self._update(from, to, ids.clone(), values.clone())?; if !to.is_zero() { - Erc1155::_check_on_erc1155_received( + self.erc1155._check_on_erc1155_received( msg::sender(), from, to, diff --git a/contracts/src/token/erc1155/extensions/uri_storage.rs b/contracts/src/token/erc1155/extensions/uri_storage.rs index cc510039c..442c260fa 100644 --- a/contracts/src/token/erc1155/extensions/uri_storage.rs +++ b/contracts/src/token/erc1155/extensions/uri_storage.rs @@ -94,7 +94,7 @@ mod tests { use stylus_sdk::prelude::storage; use super::Erc1155UriStorage; - use crate::token::erc1155::extensions::Erc1155MetadataUri; + use crate::token::erc1155::{extensions::Erc1155MetadataUri, Erc1155}; fn random_token_id() -> U256 { let num: u32 = rand::random(); diff --git a/contracts/src/token/erc1155/mod.rs b/contracts/src/token/erc1155/mod.rs index 5a0e1c768..c0dd5dcfd 100644 --- a/contracts/src/token/erc1155/mod.rs +++ b/contracts/src/token/erc1155/mod.rs @@ -8,7 +8,7 @@ use stylus_sdk::{ call::{self, Call, MethodError}, evm, function_selector, msg, prelude::{public, storage, AddressVM, SolidityError}, - storage::{StorageBool, StorageMap, StorageU256}, + storage::{StorageBool, StorageMap, StorageU256, TopLevelStorage}, }; use crate::utils::{ @@ -194,6 +194,11 @@ pub struct Erc1155 { StorageMap>, } +/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when +/// calling other contracts and not `&mut (impl TopLevelStorage + +/// BorrowMut)`. Should be fixed in the future by the Stylus team. +unsafe impl TopLevelStorage for Erc1155 {} + /// Required interface of an [`Erc1155`] compliant contract. #[interface_id] pub trait IErc1155 { @@ -557,7 +562,7 @@ impl Erc1155 { self._update(from, to, ids.clone(), values.clone())?; if !to.is_zero() { - Erc1155::_check_on_erc1155_received( + self._check_on_erc1155_received( msg::sender(), from, to, @@ -767,6 +772,7 @@ impl Erc1155 { /// /// # Arguments /// + /// * `&mut self` - Write access to the contract's state. /// * `operator` - Generally the address that initiated the token transfer /// (e.g. `msg::sender()`). /// * `from` - Account of the sender. @@ -785,6 +791,7 @@ impl Erc1155 { /// interface id or returned with error, then the error /// [`Error::InvalidReceiver`] is returned. fn _check_on_erc1155_received( + &mut self, operator: Address, from: Address, to: Address, @@ -796,7 +803,7 @@ impl Erc1155 { } let receiver = IERC1155Receiver::new(to); - let call = Call::new(); + let call = Call::new_in(self); let result = match details.transfer { Transfer::Single { id, value } => receiver .on_erc_1155_received(call, operator, from, id, value, data), diff --git a/contracts/src/token/erc20/extensions/permit.rs b/contracts/src/token/erc20/extensions/permit.rs index 2135b2a54..28d7bcf46 100644 --- a/contracts/src/token/erc20/extensions/permit.rs +++ b/contracts/src/token/erc20/extensions/permit.rs @@ -17,6 +17,7 @@ use alloy_sol_types::SolType; use stylus_sdk::{ block, prelude::{storage, StorageType}, + storage::TopLevelStorage, stylus_proc::{public, SolidityError}, }; @@ -81,6 +82,11 @@ pub struct Erc20Permit { pub eip712: T, } +/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when +/// calling other contracts and not `&mut (impl TopLevelStorage + +/// BorrowMut)`. Should be fixed in the future by the Stylus team. +unsafe impl TopLevelStorage for Erc20Permit {} + #[public] impl Erc20Permit { /// Returns the current nonce for `owner`. @@ -171,7 +177,7 @@ impl Erc20Permit { let hash: B256 = self.eip712.hash_typed_data_v4(struct_hash); - let signer: Address = ecdsa::recover(hash, v, r, s)?; + let signer: Address = ecdsa::recover(self, hash, v, r, s)?; if signer != owner { return Err(ERC2612InvalidSigner { signer, owner }.into()); diff --git a/contracts/src/token/erc20/utils/safe_erc20.rs b/contracts/src/token/erc20/utils/safe_erc20.rs index 1bb611e77..20bb84e9e 100644 --- a/contracts/src/token/erc20/utils/safe_erc20.rs +++ b/contracts/src/token/erc20/utils/safe_erc20.rs @@ -18,6 +18,7 @@ use stylus_sdk::{ evm::gas_left, function_selector, prelude::storage, + storage::TopLevelStorage, stylus_proc::{public, SolidityError}, types::AddressVM, }; @@ -87,6 +88,11 @@ mod token { #[storage] pub struct SafeErc20 {} +/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when +/// calling other contracts and not `&mut (impl TopLevelStorage + +/// BorrowMut)`. Should be fixed in the future by the Stylus team. +unsafe impl TopLevelStorage for SafeErc20 {} + /// Required interface of a [`SafeErc20`] utility contract. pub trait ISafeErc20 { /// The error type associated to this trait implementation. diff --git a/contracts/src/token/erc721/extensions/consecutive.rs b/contracts/src/token/erc721/extensions/consecutive.rs index a1297b321..c0c458fa6 100644 --- a/contracts/src/token/erc721/extensions/consecutive.rs +++ b/contracts/src/token/erc721/extensions/consecutive.rs @@ -30,7 +30,7 @@ use alloy_primitives::{uint, Address, U256}; use stylus_sdk::{ abi::Bytes, evm, msg, - prelude::storage, + prelude::{storage, TopLevelStorage}, stylus_proc::{public, SolidityError}, }; @@ -140,6 +140,8 @@ pub enum Error { ForbiddenBatchBurn(ERC721ForbiddenBatchBurn), } +unsafe impl TopLevelStorage for Erc721Consecutive {} + // ************** ERC-721 External ************** #[public] diff --git a/contracts/src/token/erc721/mod.rs b/contracts/src/token/erc721/mod.rs index 917bf5d1e..2747366cd 100644 --- a/contracts/src/token/erc721/mod.rs +++ b/contracts/src/token/erc721/mod.rs @@ -207,6 +207,11 @@ pub struct Erc721 { StorageMap>, } +/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when +/// calling other contracts and not `&mut (impl TopLevelStorage + +/// BorrowMut)`. Should be fixed in the future by the Stylus team. +unsafe impl TopLevelStorage for Erc721 {} + /// Required interface of an [`Erc721`] compliant contract. #[interface_id] pub trait IErc721 { @@ -1099,7 +1104,7 @@ impl Erc721 { /// /// # Arguments /// - /// * `&self` - Read access to the contract's state. + /// * `&mut self` - Write access to the contract's state. /// * `operator` - Account to add to the set of authorized operators. /// * `from` - Account of the sender. /// * `to` - Account of the recipient. @@ -1113,7 +1118,7 @@ impl Erc721 { /// interface id or returned with error, then the error /// [`Error::InvalidReceiver`] is returned. pub fn _check_on_erc721_received( - &self, + &mut self, operator: Address, from: Address, to: Address, @@ -1125,7 +1130,7 @@ impl Erc721 { } let receiver = IERC721Receiver::new(to); - let call = Call::new(); + let call = Call::new_in(self); let result = receiver.on_erc_721_received( call, operator, diff --git a/contracts/src/utils/cryptography/ecdsa.rs b/contracts/src/utils/cryptography/ecdsa.rs index 9830defc0..5f8c6bf6a 100644 --- a/contracts/src/utils/cryptography/ecdsa.rs +++ b/contracts/src/utils/cryptography/ecdsa.rs @@ -8,6 +8,7 @@ use alloy_primitives::{address, uint, Address, B256, U256}; use alloy_sol_types::SolType; use stylus_sdk::{ call::{self, Call, MethodError}, + storage::TopLevelStorage, stylus_proc::SolidityError, }; @@ -76,6 +77,7 @@ impl MethodError for ecdsa::Error { /// /// # Arguments /// +/// * `storage` - Write access to storage. /// * `hash` - Hash of the message. /// * `v` - `v` value from the signature. /// * `r` - `r` value from the signature. @@ -91,10 +93,16 @@ impl MethodError for ecdsa::Error { /// # Panics /// /// * If the `ecrecover` precompile fails to execute. -pub fn recover(hash: B256, v: u8, r: B256, s: B256) -> Result { +pub fn recover( + storage: &mut impl TopLevelStorage, + hash: B256, + v: u8, + r: B256, + s: B256, +) -> Result { check_if_malleable(&s)?; // If the signature is valid (and not malleable), return the signer address. - _recover(hash, v, r, s) + _recover(storage, hash, v, r, s) } /// Calls `ecrecover` EVM precompile. @@ -104,6 +112,7 @@ pub fn recover(hash: B256, v: u8, r: B256, s: B256) -> Result { /// /// # Arguments /// +/// * `storage` - Write access to storage. /// * `hash` - Hash of the message. /// * `v` - `v` value from the signature. /// * `r` - `r` value from the signature. @@ -119,7 +128,13 @@ pub fn recover(hash: B256, v: u8, r: B256, s: B256) -> Result { /// # Panics /// /// * If the `ecrecover` precompile fails to execute. -fn _recover(hash: B256, v: u8, r: B256, s: B256) -> Result { +fn _recover( + storage: &mut impl TopLevelStorage, + hash: B256, + v: u8, + r: B256, + s: B256, +) -> Result { let calldata = encode_calldata(hash, v, r, s); if v == 0 || v == 1 { @@ -130,8 +145,9 @@ fn _recover(hash: B256, v: u8, r: B256, s: B256) -> Result { return Err(ECDSAInvalidSignature {}.into()); } - let recovered = call::static_call(Call::new(), ECRECOVER_ADDR, &calldata) - .expect("should call `ecrecover` precompile"); + let recovered = + call::static_call(Call::new_in(storage), ECRECOVER_ADDR, &calldata) + .expect("should call `ecrecover` precompile"); let recovered = Address::from_slice(&recovered[12..]); diff --git a/examples/ecdsa/src/lib.rs b/examples/ecdsa/src/lib.rs index 3c4fb31c5..f94e0a5da 100644 --- a/examples/ecdsa/src/lib.rs +++ b/examples/ecdsa/src/lib.rs @@ -20,7 +20,7 @@ impl ECDSAExample { r: B256, s: B256, ) -> Result> { - let signer = ecdsa::recover(hash, v, r, s)?; + let signer = ecdsa::recover(self, hash, v, r, s)?; Ok(signer) } } diff --git a/lib/motsu-proc/Cargo.toml b/lib/motsu-proc/Cargo.toml deleted file mode 100644 index 58375a3ea..000000000 --- a/lib/motsu-proc/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "motsu-proc" -description = "Mostu's Procedural Macros" -edition.workspace = true -categories = ["development-tools::testing", "cryptography::cryptocurrencies"] -keywords = ["arbitrum", "ethereum", "stylus", "unit-tests", "tests"] -license.workspace = true -repository.workspace = true -version = "0.2.0" - -[dependencies] -proc-macro2.workspace = true -quote.workspace = true -syn.workspace = true - -[dev-dependencies] -motsu.workspace = true -alloy-primitives.workspace = true -alloy-sol-types.workspace = true -stylus-sdk.workspace = true - -[lib] -proc-macro = true - -[lints] -workspace = true diff --git a/lib/motsu-proc/README.md b/lib/motsu-proc/README.md deleted file mode 100644 index 40d2ef4b1..000000000 --- a/lib/motsu-proc/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Motsu's Procedural Macros - -This crate contains procedural macros used in [`motsu`]. - -[motsu]: ../motsu/README.md diff --git a/lib/motsu-proc/src/lib.rs b/lib/motsu-proc/src/lib.rs deleted file mode 100644 index c58093e86..000000000 --- a/lib/motsu-proc/src/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! Procedural macro definitions used in `motsu`. -use proc_macro::TokenStream; - -/// Shorthand to print nice errors. -/// -/// Note that it's defined before the module declarations. -macro_rules! error { - ($tokens:expr, $($msg:expr),+ $(,)?) => {{ - let error = syn::Error::new(syn::spanned::Spanned::span(&$tokens), format!($($msg),+)); - return error.to_compile_error().into(); - }}; - (@ $tokens:expr, $($msg:expr),+ $(,)?) => {{ - return Err(syn::Error::new(syn::spanned::Spanned::span(&$tokens), format!($($msg),+))) - }}; -} - -mod test; - -/// Defines a unit test that provides access to Stylus' execution context. -/// -/// Internally, this is a thin wrapper over `#[test]` that gives access to -/// affordances like contract storage and `msg::sender`. If you don't need -/// them, you can pass no arguments to the test function or simply use -/// `#[test]` instead of `#[motsu::test]`. -/// -/// # Examples -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod tests { -/// #[motsu::test] -/// fn reads_balance(contract: Erc20) { -/// let balance = contract.balance_of(Address::ZERO); -/// assert_eq!(U256::ZERO, balance); -/// -/// let owner = msg::sender(); -/// let one = U256::from(1); -/// contract._balances.setter(owner).set(one); -/// let balance = contract.balance_of(owner); -/// assert_eq!(one, balance); -/// } -/// } -/// ``` -/// -/// ```rust,ignore -/// #[cfg(test)] -/// mod tests { -/// #[motsu::test] -/// fn t() { // If no params, it expands to a `#[test]`. -/// ... -/// } -/// } -/// ``` -#[proc_macro_attribute] -pub fn test(attr: TokenStream, input: TokenStream) -> TokenStream { - test::test(&attr, input) -} diff --git a/lib/motsu-proc/src/test.rs b/lib/motsu-proc/src/test.rs deleted file mode 100644 index 2e88961cd..000000000 --- a/lib/motsu-proc/src/test.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Defines the `#[motsu::test]` procedural macro. -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, FnArg}; - -/// Defines a unit test that provides access to Stylus execution context. -/// -/// For more information see [`crate::test`]. -pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { - let item_fn = parse_macro_input!(input as syn::ItemFn); - let attrs = &item_fn.attrs; - let sig = &item_fn.sig; - let fn_name = &sig.ident; - let fn_return_type = &sig.output; - let fn_block = &item_fn.block; - let fn_args = &sig.inputs; - - // Currently, more than one contract per unit test is not supported. - if fn_args.len() > 1 { - error!(fn_args, "expected at most one contract in test signature"); - } - - // Whether 1 or none contracts will be declared. - let arg_binding_and_ty = match fn_args - .into_iter() - .map(|arg| { - let FnArg::Typed(arg) = arg else { - error!(@arg, "unexpected receiver argument in test signature"); - }; - let contract_arg_binding = &arg.pat; - let contract_ty = &arg.ty; - Ok((contract_arg_binding, contract_ty)) - }) - .collect::, _>>() - { - Ok(res) => res, - Err(err) => return err.to_compile_error().into(), - }; - - let contract_arg_defs = - arg_binding_and_ty.iter().map(|(arg_binding, contract_ty)| { - // Test case assumes, that contract's variable has `&mut` reference - // to contract's type. - quote! { - #arg_binding: &mut #contract_ty - } - }); - - let contract_args = - arg_binding_and_ty.iter().map(|(_arg_binding, contract_ty)| { - // Pass mutable reference to the contract. - quote! { - &mut <#contract_ty>::default() - } - }); - - // Declare test case closure. - // Pass mut ref to the test closure and call it. - // Reset storage for the test context and return test's output. - quote! { - #( #attrs )* - #[test] - fn #fn_name() #fn_return_type { - use ::motsu::prelude::DefaultStorage; - let test = | #( #contract_arg_defs ),* | #fn_block; - let res = test( #( #contract_args ),* ); - ::motsu::prelude::Context::current().reset_storage(); - res - } - } - .into() -} diff --git a/lib/motsu/Cargo.toml b/lib/motsu/Cargo.toml deleted file mode 100644 index 2bfa39776..000000000 --- a/lib/motsu/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "motsu" -description = "Unit Testing for Stylus" -edition.workspace = true -categories = ["development-tools::testing", "cryptography::cryptocurrencies"] -keywords = ["arbitrum", "ethereum", "stylus", "unit-tests", "tests"] -license.workspace = true -repository.workspace = true -version = "0.2.0" - -[dependencies] -const-hex.workspace = true -once_cell.workspace = true -tiny-keccak.workspace = true -stylus-sdk.workspace = true -motsu-proc.workspace = true -dashmap.workspace = true - -[lints] -workspace = true diff --git a/lib/motsu/README.md b/lib/motsu/README.md deleted file mode 100644 index f7f10674e..000000000 --- a/lib/motsu/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Motsu (持つ) - Unit Testing for Stylus - -This crate enables unit-testing for Stylus contracts. It abstracts away the -machinery necessary for writing tests behind a `#[motsu::test]` procedural -macro. - -`motsu` means ["to hold"](https://jisho.org/word/%E6%8C%81%E3%81%A4) in -Japanese -- we hold a stylus in our hand. - -## Usage - -Annotate tests with `#[motsu::test]` instead of `#[test]` to get access to VM -affordances. - -Note that we require contracts to implement `stylus_sdk::prelude::StorageType`. -This trait is typically implemented by default with `stylus_proc::sol_storage` -or `stylus_proc::storage` macros. - -```rust -#[cfg(test)] -mod tests { - use contracts::token::erc20::Erc20; - - #[motsu::test] - fn reads_balance(contract: Erc20) { - let balance = contract.balance_of(Address::ZERO); // Access storage. - assert_eq!(balance, U256::ZERO); - } -} -``` - -Annotating a test function that accepts no parameters will make `#[motsu::test]` -behave the same as `#[test]`. - -```rust,ignore -#[cfg(test)] -mod tests { - #[motsu::test] - fn t() { // If no params, it expands to a `#[test]`. - // ... - } -} -``` - -Note that currently, test suites using `motsu::test` will run serially because -of global access to storage. - -### Notice - -We maintain this crate on a best-effort basis. We use it extensively on our own -tests, so we will add here any symbols we may need. However, since we expect -this to be a temporary solution, don't expect us to address all requests. - -That being said, please do open an issue to start a discussion, keeping in mind -our [code of conduct] and [contribution guidelines]. - -[code of conduct]: ../../CODE_OF_CONDUCT.md - -[contribution guidelines]: ../../CONTRIBUTING.md - -## Security - -Refer to our [Security Policy](../../SECURITY.md) for more details. diff --git a/lib/motsu/src/context.rs b/lib/motsu/src/context.rs deleted file mode 100644 index 381d504b9..000000000 --- a/lib/motsu/src/context.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Unit-testing context for Stylus contracts. - -use std::{collections::HashMap, ptr}; - -use dashmap::DashMap; -use once_cell::sync::Lazy; -use stylus_sdk::{alloy_primitives::uint, prelude::StorageType}; - -use crate::prelude::{Bytes32, WORD_BYTES}; - -/// Context of stylus unit tests associated with the current test thread. -#[allow(clippy::module_name_repetitions)] -pub struct Context { - thread_name: ThreadName, -} - -impl Context { - /// Get test context associated with the current test thread. - #[must_use] - pub fn current() -> Self { - Self { thread_name: ThreadName::current() } - } - - /// Get the value at `key` in storage. - pub(crate) fn get_bytes(self, key: &Bytes32) -> Bytes32 { - let storage = STORAGE.entry(self.thread_name).or_default(); - storage.contract_data.get(key).copied().unwrap_or_default() - } - - /// Get the raw value at `key` in storage and write it to `value`. - pub(crate) unsafe fn get_bytes_raw(self, key: *const u8, value: *mut u8) { - let key = read_bytes32(key); - - write_bytes32(value, self.get_bytes(&key)); - } - - /// Set the value at `key` in storage to `value`. - pub(crate) fn set_bytes(self, key: Bytes32, value: Bytes32) { - let mut storage = STORAGE.entry(self.thread_name).or_default(); - storage.contract_data.insert(key, value); - } - - /// Set the raw value at `key` in storage to `value`. - pub(crate) unsafe fn set_bytes_raw(self, key: *const u8, value: *const u8) { - let (key, value) = (read_bytes32(key), read_bytes32(value)); - self.set_bytes(key, value); - } - - /// Clears storage, removing all key-value pairs associated with the current - /// test thread. - pub fn reset_storage(self) { - STORAGE.remove(&self.thread_name); - } -} - -/// Storage mock: A global mutable key-value store. -/// Allows concurrent access. -/// -/// The key is the name of the test thread, and the value is the storage of the -/// test case. -static STORAGE: Lazy> = - Lazy::new(DashMap::new); - -/// Test thread name metadata. -#[derive(Clone, Eq, PartialEq, Hash)] -struct ThreadName(String); - -impl ThreadName { - /// Get the name of the current test thread. - fn current() -> Self { - let current_thread_name = std::thread::current() - .name() - .expect("should retrieve current thread name") - .to_string(); - Self(current_thread_name) - } -} - -/// Storage for unit test's mock data. -#[derive(Default)] -struct MockStorage { - /// Contract's mock data storage. - contract_data: HashMap, -} - -/// Read the word from location pointed by `ptr`. -unsafe fn read_bytes32(ptr: *const u8) -> Bytes32 { - let mut res = Bytes32::default(); - ptr::copy(ptr, res.as_mut_ptr(), WORD_BYTES); - res -} - -/// Write the word `bytes` to the location pointed by `ptr`. -unsafe fn write_bytes32(ptr: *mut u8, bytes: Bytes32) { - ptr::copy(bytes.as_ptr(), ptr, WORD_BYTES); -} - -/// Initializes fields of contract storage and child contract storages with -/// default values. -pub trait DefaultStorage: StorageType { - /// Initializes fields of contract storage and child contract storages with - /// default values. - #[must_use] - fn default() -> Self { - unsafe { Self::new(uint!(0_U256), 0) } - } -} - -impl DefaultStorage for ST {} diff --git a/lib/motsu/src/lib.rs b/lib/motsu/src/lib.rs deleted file mode 100644 index 7636b7541..000000000 --- a/lib/motsu/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! # Motsu - Unit Testing for Stylus -//! -//! This crate enables unit-testing for Stylus contracts. It abstracts away the -//! machinery necessary for writing tests behind a -//! [`#[motsu::test]`][test_attribute] procedural macro. -//! -//! The name `motsu` is an analogy to the place where you put your fingers to -//! hold a stylus pen. -//! -//! ## Usage -//! -//! Annotate tests with [`#[motsu::test]`][test_attribute] instead of `#[test]` -//! to get access to VM affordances. -//! -//! Note that we require contracts to implement -//! `stylus_sdk::prelude::StorageType`. This trait is typically implemented by -//! default with `stylus_proc::sol_storage` or `stylus_proc::storage` macros. -//! -//! ```rust -//! #[cfg(test)] -//! mod tests { -//! use contracts::token::erc20::Erc20; -//! -//! #[motsu::test] -//! fn reads_balance(contract: Erc20) { -//! let balance = contract.balance_of(Address::ZERO); // Access storage. -//! assert_eq!(balance, U256::ZERO); -//! } -//! } -//! ``` -//! -//! Annotating a test function that accepts no parameters will make -//! [`#[motsu::test]`][test_attribute] behave the same as `#[test]`. -//! -//! ```rust,ignore -//! #[cfg(test)] -//! mod tests { -//! #[motsu::test] // Equivalent to #[test] -//! fn test_fn() { -//! ... -//! } -//! } -//! ``` -//! -//! [test_attribute]: crate::test -mod context; -pub mod prelude; -mod shims; - -pub use motsu_proc::test; diff --git a/lib/motsu/src/prelude.rs b/lib/motsu/src/prelude.rs deleted file mode 100644 index 5c8798297..000000000 --- a/lib/motsu/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Common imports for `motsu` tests. -pub use crate::{ - context::{Context, DefaultStorage}, - shims::*, -}; diff --git a/lib/motsu/src/shims.rs b/lib/motsu/src/shims.rs deleted file mode 100644 index 162f9e2fd..000000000 --- a/lib/motsu/src/shims.rs +++ /dev/null @@ -1,346 +0,0 @@ -//! Shims that mock common host imports in Stylus `wasm` programs. -//! -//! Most of the documentation is taken from the [Stylus source]. -//! -//! We allow unsafe here because safety is guaranteed by the Stylus team. -//! -//! [Stylus source]: https://github.com/OffchainLabs/stylus/blob/484efac4f56fb70f96d4890748b8ec2543d88acd/arbitrator/wasm-libraries/user-host-trait/src/lib.rs -//! -//! ## Motivation -//! -//! Without these shims we can't currently run unit tests for stylus contracts, -//! since the symbols the compiled binaries expect to find are not there. -//! -//! If you run `cargo test` on a fresh Stylus project, it will error with: -//! -//! ```terminal -//! dyld[97792]: missing symbol called -//! ``` -//! -//! This crate is a temporary solution until the Stylus team provides us with a -//! different and more stable mechanism for unit-testing our contracts. -//! -//! ## Usage -//! -//! Import these shims in your test modules as `motsu::prelude::*` to populate -//! the namespace with the appropriate symbols. -//! -//! ```rust,ignore -//! #[cfg(test)] -//! mod tests { -//! use contracts::token::erc20::Erc20; -//! -//! #[motsu::test] -//! fn reads_balance(contract: Erc20) { -//! let balance = contract.balance_of(Address::ZERO); // Access storage. -//! assert_eq!(balance, U256::ZERO); -//! } -//! } -//! ``` -#![allow(clippy::missing_safety_doc)] -use std::slice; - -use tiny_keccak::{Hasher, Keccak}; - -use crate::context::Context; - -pub(crate) const WORD_BYTES: usize = 32; -pub(crate) type Bytes32 = [u8; WORD_BYTES]; - -/// Efficiently computes the [`keccak256`] hash of the given preimage. -/// The semantics are equivalent to that of the EVM's [`SHA3`] opcode. -/// -/// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3 -/// [`SHA3`]: https://www.evm.codes/#20 -#[no_mangle] -pub unsafe extern "C" fn native_keccak256( - bytes: *const u8, - len: usize, - output: *mut u8, -) { - let mut hasher = Keccak::v256(); - - let data = slice::from_raw_parts(bytes, len); - hasher.update(data); - - let output = slice::from_raw_parts_mut(output, WORD_BYTES); - hasher.finalize(output); -} - -/// Reads a 32-byte value from permanent storage. -/// -/// Stylus's storage format is identical to that of the EVM. This means that, -/// under the hood, this hostio is accessing the 32-byte value stored in the EVM -/// state trie at offset `key`, which will be `0` when not previously set. The -/// semantics, then, are equivalent to that of the EVM's [`SLOAD`] opcode. -/// -/// [`SLOAD`]: https://www.evm.codes/#54 -/// -/// # Panics -/// -/// May panic if unable to lock `STORAGE`. -#[no_mangle] -pub unsafe extern "C" fn storage_load_bytes32(key: *const u8, out: *mut u8) { - Context::current().get_bytes_raw(key, out); -} - -/// Writes a 32-byte value to the permanent storage cache. -/// -/// Stylus's storage format is identical to that of the EVM. This means that, -/// under the hood, this hostio represents storing a 32-byte value into the EVM -/// state trie at offset `key`. Refunds are tabulated exactly as in the EVM. The -/// semantics, then, are equivalent to that of the EVM's [`SSTORE`] opcode. -/// -/// Note: because the value is cached, one must call `storage_flush_cache` to -/// persist it. -/// -/// [`SSTORE`]: https://www.evm.codes/#55 -/// -/// # Panics -/// -/// May panic if unable to lock `STORAGE`. -#[no_mangle] -pub unsafe extern "C" fn storage_cache_bytes32( - key: *const u8, - value: *const u8, -) { - Context::current().set_bytes_raw(key, value); -} - -/// Persists any dirty values in the storage cache to the EVM state trie, -/// dropping the cache entirely if requested. Analogous to repeated invocations -/// of [`SSTORE`]. -/// -/// [`SSTORE`]: https://www.evm.codes/#55 -pub fn storage_flush_cache(_: bool) { - // No-op: we don't use the cache in our unit-tests. -} - -/// Dummy msg sender set for tests. -pub const MSG_SENDER: &[u8; 42] = b"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"; - -/// Dummy contract address set for tests. -pub const CONTRACT_ADDRESS: &[u8; 42] = - b"0xdCE82b5f92C98F27F116F70491a487EFFDb6a2a9"; - -/// Arbitrum's CHAID ID. -pub const CHAIN_ID: u64 = 42161; - -/// Externally Owned Account (EOA) code hash. -pub const EOA_CODEHASH: &[u8; 66] = - b"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; - -/// Gets the address of the account that called the program. -/// -/// For normal L2-to-L2 transactions the semantics are equivalent to that of the -/// EVM's [`CALLER`] opcode, including in cases arising from [`DELEGATE_CALL`]. -/// -/// For L1-to-L2 retryable ticket transactions, the top-level sender's address -/// will be aliased. See [`Retryable Ticket Address Aliasing`][aliasing] for -/// more information on how this works. -/// -/// [`CALLER`]: https://www.evm.codes/#33 -/// [`DELEGATE_CALL`]: https://www.evm.codes/#f4 -/// [aliasing]: https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing -/// -/// # Panics -/// -/// May panic if fails to parse `MSG_SENDER` as an address. -#[no_mangle] -pub unsafe extern "C" fn msg_sender(sender: *mut u8) { - let addr = const_hex::const_decode_to_array::<20>(MSG_SENDER).unwrap(); - std::ptr::copy(addr.as_ptr(), sender, 20); -} - -/// Gets the address of the current program. The semantics are equivalent to -/// that of the EVM's [`ADDRESS`] opcode. -/// -/// [`ADDRESS`]: https://www.evm.codes/#30 -/// -/// # Panics -/// -/// May panic if fails to parse `CONTRACT_ADDRESS` as an address. -#[no_mangle] -pub unsafe extern "C" fn contract_address(address: *mut u8) { - let addr = - const_hex::const_decode_to_array::<20>(CONTRACT_ADDRESS).unwrap(); - std::ptr::copy(addr.as_ptr(), address, 20); -} - -/// Gets the chain ID of the current chain. The semantics are equivalent to -/// that of the EVM's [`CHAINID`] opcode. -/// -/// [`CHAINID`]: https://www.evm.codes/#46 -#[no_mangle] -pub unsafe extern "C" fn chainid() -> u64 { - CHAIN_ID -} - -/// Emits an EVM log with the given number of topics and data, the first bytes -/// of which should be the 32-byte-aligned topic data. -/// -/// The semantics are equivalent to that of the EVM's [`LOG0`], [`LOG1`], -/// [`LOG2`], [`LOG3`], and [`LOG4`] opcodes based on the number of topics -/// specified. Requesting more than `4` topics will induce a revert. -/// -/// [`LOG0`]: https://www.evm.codes/#a0 -/// [`LOG1`]: https://www.evm.codes/#a1 -/// [`LOG2`]: https://www.evm.codes/#a2 -/// [`LOG3`]: https://www.evm.codes/#a3 -/// [`LOG4`]: https://www.evm.codes/#a4 -#[no_mangle] -pub unsafe extern "C" fn emit_log(_: *const u8, _: usize, _: usize) { - // No-op: we don't check for events in our unit-tests. -} - -/// Gets the code hash of the account at the given address. -/// -/// The semantics are equivalent to that of the EVM's [`EXT_CODEHASH`] opcode. -/// Note that the code hash of an account without code will be the empty hash -/// `keccak("") = -/// c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. -/// -/// [`EXT_CODEHASH`]: https://www.evm.codes/#3F -/// -/// # Panics -/// -/// May panic if fails to parse `ACCOUNT_CODEHASH` as a keccack hash. -#[no_mangle] -pub unsafe extern "C" fn account_codehash(_address: *const u8, dest: *mut u8) { - let account_codehash = - const_hex::const_decode_to_array::<32>(EOA_CODEHASH).unwrap(); - - std::ptr::copy(account_codehash.as_ptr(), dest, 32); -} - -/// Returns the length of the last EVM call or deployment return result, or `0` -/// if neither have happened during the program's execution. -/// -/// The semantics are equivalent to that of the EVM's [`RETURN_DATA_SIZE`] -/// opcode. -/// -/// [`RETURN_DATA_SIZE`]: https://www.evm.codes/#3d -#[no_mangle] -pub unsafe extern "C" fn return_data_size() -> usize { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Copies the bytes of the last EVM call or deployment return result. -/// -/// Does not revert if out of bounds, but rather copies the overlapping portion. -/// The semantics are otherwise equivalent to that of the EVM's -/// [`RETURN_DATA_COPY`] opcode. -/// -/// Returns the number of bytes written. -/// -/// [`RETURN_DATA_COPY`]: https://www.evm.codes/#3e -#[no_mangle] -pub unsafe extern "C" fn read_return_data( - _dest: *mut u8, - _offset: usize, - _size: usize, -) -> usize { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Calls the contract at the given address with options for passing value and -/// to limit the amount of gas supplied. The return status indicates whether the -/// call succeeded, and is nonzero on failure. -/// -/// In both cases `return_data_len` will store the length of the result, the -/// bytes of which can be read via the `read_return_data` hostio. The bytes are -/// not returned directly so that the programmer can potentially save gas by -/// choosing which subset of the return result they'd like to copy. -/// -/// The semantics are equivalent to that of the EVM's [`CALL`] opcode, including -/// callvalue stipends and the 63/64 gas rule. This means that supplying the -/// `u64::MAX` gas can be used to send as much as possible. -/// -/// [`CALL`]: https://www.evm.codes/#f1 -#[no_mangle] -pub unsafe extern "C" fn call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, - _value: *const u8, - _gas: u64, - _return_data_len: *mut usize, -) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Static calls the contract at the given address, with the option to limit the -/// amount of gas supplied. The return status indicates whether the call -/// succeeded, and is nonzero on failure. -/// -/// In both cases `return_data_len` will store the length of the result, the -/// bytes of which can be read via the `read_return_data` hostio. The bytes are -/// not returned directly so that the programmer can potentially save gas by -/// choosing which subset of the return result they'd like to copy. -/// -/// The semantics are equivalent to that of the EVM's [`STATIC_CALL`] opcode, -/// including the 63/64 gas rule. This means that supplying `u64::MAX` gas can -/// be used to send as much as possible. -/// -/// [`STATIC_CALL`]: https://www.evm.codes/#FA -#[no_mangle] -pub unsafe extern "C" fn static_call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, - _gas: u64, - _return_data_len: *mut usize, -) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Delegate calls the contract at the given address, with the option to limit -/// the amount of gas supplied. The return status indicates whether the call -/// succeeded, and is nonzero on failure. -/// -/// In both cases `return_data_len` will store the length of the result, the -/// bytes of which can be read via the `read_return_data` hostio. The bytes are -/// not returned directly so that the programmer can potentially save gas by -/// choosing which subset of the return result they'd like to copy. -/// -/// The semantics are equivalent to that of the EVM's [`DELEGATE_CALL`] opcode, -/// including the 63/64 gas rule. This means that supplying `u64::MAX` gas can -/// be used to send as much as possible. -/// -/// [`DELEGATE_CALL`]: https://www.evm.codes/#F4 -#[no_mangle] -pub unsafe extern "C" fn delegate_call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, - _gas: u64, - _return_data_len: *mut usize, -) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 -} - -/// Gets a bounded estimate of the Unix timestamp at which the Sequencer -/// sequenced the transaction. See [`Block Numbers and Time`] for more -/// information on how this value is determined. -/// -/// [`Block Numbers and Time`]: https://developer.arbitrum.io/time -#[no_mangle] -pub unsafe extern "C" fn block_timestamp() -> u64 { - // Epoch timestamp: 1st January 2025 00::00::00 - 1_735_689_600 -}