From 5030d89738ceaaaf8192b82bdcbfb9e449beeaea Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Fri, 24 Oct 2025 00:09:12 -0300 Subject: [PATCH 01/36] implement `gas_limit` host fn --- crates/env/src/api.rs | 5 +++ crates/env/src/backend.rs | 7 +++++ .../env/src/engine/on_chain/pallet_revive.rs | 4 +++ crates/ink/src/env_access.rs | 31 +++++++++++++++++++ .../ink/tests/ui/contract/pass/env-access.rs | 2 ++ 5 files changed, 49 insertions(+) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index c4d1aaacec3..728f057da77 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -73,6 +73,11 @@ pub fn caller() -> Address { ::on_instance(TypedEnvBackend::caller) } +/// Returns the block ref_time limit. +pub fn gas_limit() -> u64 { + ::on_instance(TypedEnvBackend::gas_limit) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 71e63a9a4df..8735e3c5f05 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -228,6 +228,13 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`caller`][`crate::caller`] fn caller(&mut self) -> Address; + /// Returns the block ref_time limit. + /// + /// # Note + /// + /// For more details visit: [`gas_limit`][`crate::gas_limit`] + fn gas_limit(&mut self) -> u64; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index e06196b0cc5..a61d1e83879 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -887,6 +887,10 @@ impl TypedEnvBackend for EnvInstance { .expect("The executed contract must have a caller with a valid account id.") } + fn gas_limit(&mut self) -> u64 { + ext::gas_limit() + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index d0544b0c539..6d5f8b0c814 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -108,6 +108,37 @@ where ink_env::caller() } + /// Returns the block ref_time limit. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_limit(&self) -> u64 { + /// self.env().gas_limit() + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::gas_limit`] + pub fn gas_limit(self) -> u64 { + ink_env::gas_limit() + } + /// Returns the transferred value for the contract execution. /// /// # Example diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index ce657d817e1..764c47103c7 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -14,6 +14,7 @@ mod contract { let _ = Self::env().block_number(); let _ = Self::env().caller(); let _ = Self::env().minimum_balance(); + let _ = Self::env().gas_limit(); let _ = Self::env().transferred_value(); let _ = Self::env().weight_to_fee(0); Self {} @@ -27,6 +28,7 @@ mod contract { let _ = self.env().block_number(); let _ = self.env().caller(); let _ = self.env().minimum_balance(); + let _ = Self.env().gas_limit(); let _ = self.env().transferred_value(); let _ = self.env().weight_to_fee(0); } From f04d729c045988a9de691608d8a7337002341544 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Fri, 24 Oct 2025 01:05:07 -0300 Subject: [PATCH 02/36] add tests for `gas_limit` host fn --- .../internal/gas-hostfns/Cargo.toml | 27 ++++++++ integration-tests/internal/gas-hostfns/lib.rs | 68 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100755 integration-tests/internal/gas-hostfns/Cargo.toml create mode 100644 integration-tests/internal/gas-hostfns/lib.rs diff --git a/integration-tests/internal/gas-hostfns/Cargo.toml b/integration-tests/internal/gas-hostfns/Cargo.toml new file mode 100755 index 00000000000..7ee2db9bc72 --- /dev/null +++ b/integration-tests/internal/gas-hostfns/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "gas_hostfns" +description = "E2E tests for gas related host functions" +version = "6.0.0-alpha.1" +authors = ["Use Ink "] +edition = "2021" +publish = false + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false, features = ["unstable-hostfn"] } + +[dev-dependencies] +ink_e2e = { path = "../../../crates/e2e" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", +] +ink-as-dependency = [] +e2e-tests = [] + +[package.metadata.ink-lang] +abi = "ink" diff --git a/integration-tests/internal/gas-hostfns/lib.rs b/integration-tests/internal/gas-hostfns/lib.rs new file mode 100644 index 00000000000..abc1dc15029 --- /dev/null +++ b/integration-tests/internal/gas-hostfns/lib.rs @@ -0,0 +1,68 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] +#![allow(clippy::new_without_default)] + +#[ink::contract] +mod gas_hostfns { + #[ink(storage)] + pub struct GasHostfns {} + + impl GasHostfns { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + /// Checks that the host function `gas_limit` works + #[ink(message)] + pub fn gas_limit(self) { + self.env.gas_limit() + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn works() { + let contract = GasHostfns::new(); + contract.gas_limit(); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn e2e_gas_limit_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate( + "gas_hostfns", + &ink_e2e::alice(), + &mut GasHostfnsRef::new(), + ) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let _call_res = client + .call(&ink_e2e::alice(), &call_builder.gas_limit()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + Ok(()) + } + } +} From 0eec69da8eb6e3d2b346723ae8fcdc62359d2d4c Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Fri, 24 Oct 2025 11:37:30 -0300 Subject: [PATCH 03/36] remove off-chain tests for `gas_limit` --- crates/env/src/engine/off_chain/impls.rs | 5 +++++ integration-tests/internal/gas-hostfns/lib.rs | 17 ++++------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 930b92164a0..cb88e8ca1b1 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -550,6 +550,11 @@ impl TypedEnvBackend for EnvInstance { .unwrap_or_else(|error| panic!("could not read `caller` property: {error:?}")) } + fn gas_limit(&mut self) -> u64 { + // Panic because of future depecration / removal + panic!(); + } + fn transferred_value(&mut self) -> U256 { self.get_property(Engine::value_transferred) .unwrap_or_else(|error| { diff --git a/integration-tests/internal/gas-hostfns/lib.rs b/integration-tests/internal/gas-hostfns/lib.rs index abc1dc15029..0711cc6fdd5 100644 --- a/integration-tests/internal/gas-hostfns/lib.rs +++ b/integration-tests/internal/gas-hostfns/lib.rs @@ -14,22 +14,11 @@ mod gas_hostfns { /// Checks that the host function `gas_limit` works #[ink(message)] - pub fn gas_limit(self) { - self.env.gas_limit() + pub fn gas_limit(&self) -> u64 { + self.env().gas_limit() } } - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn works() { - let contract = GasHostfns::new(); - contract.gas_limit(); - } - } - #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; @@ -61,6 +50,8 @@ mod gas_hostfns { .unwrap_or_else(|err| { panic!("call failed: {:#?}", err); }); + + assert!(_call_res.return_value() > 0); Ok(()) } From 95162a99e6a35faba495c556e7c09f018e01d022 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Fri, 24 Oct 2025 11:45:21 -0300 Subject: [PATCH 04/36] add changes to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56489af38e5..99b18543920 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased] ### Added +- Implements the API for the `pallet-revive` host function `gas_limit` - [#2691](https://github.com/use-ink/ink/pull/2691) - Implements the API for the `pallet-revive` host function `to_account_id` - [#2578](https://github.com/use-ink/ink/pull/2578) - Add `#[ink::contract_ref]` attribute - [#2648](https://github.com/use-ink/ink/pull/2648) - Add `ink_revive_types` (and remove `pallet-revive` dependency from `ink_e2e`) - [#2657](https://github.com/use-ink/ink/pull/2657) From 1e63416f20ef29c675f7ffcee8bac2a6d4f223b9 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sat, 25 Oct 2025 21:11:49 -0300 Subject: [PATCH 05/36] run formatting --- crates/env/src/api.rs | 2 +- crates/ink/src/env_access.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 728f057da77..a72868452bf 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -75,7 +75,7 @@ pub fn caller() -> Address { /// Returns the block ref_time limit. pub fn gas_limit() -> u64 { - ::on_instance(TypedEnvBackend::gas_limit) + ::on_instance(TypedEnvBackend::gas_limit) } /// Returns the transferred value for the contract execution. diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 6d5f8b0c814..4f990d84865 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -117,13 +117,13 @@ where /// mod my_contract { /// #[ink(storage)] /// pub struct MyContract; - /// + /// /// impl MyContract { /// #[ink(constructor)] /// pub fn new() -> Self { /// Self {} /// } - /// + /// /// #[ink(message)] /// pub fn get_limit(&self) -> u64 { /// self.env().gas_limit() From 9c3ab4177e2bc37790048f019fc743a51dfa82ba Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sat, 25 Oct 2025 21:24:33 -0300 Subject: [PATCH 06/36] fix error in env-access ui test --- crates/ink/tests/ui/contract/pass/env-access.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index 764c47103c7..e2afe0b8b40 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -28,7 +28,7 @@ mod contract { let _ = self.env().block_number(); let _ = self.env().caller(); let _ = self.env().minimum_balance(); - let _ = Self.env().gas_limit(); + let _ = self.env().gas_limit(); let _ = self.env().transferred_value(); let _ = self.env().weight_to_fee(0); } From e0c83515a2a6035c2ca6c8dfb846f8eaea903f55 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Mon, 27 Oct 2025 13:27:46 -0300 Subject: [PATCH 07/36] run formatting --- integration-tests/internal/gas-hostfns/lib.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/integration-tests/internal/gas-hostfns/lib.rs b/integration-tests/internal/gas-hostfns/lib.rs index 0711cc6fdd5..88e46d5ec5d 100644 --- a/integration-tests/internal/gas-hostfns/lib.rs +++ b/integration-tests/internal/gas-hostfns/lib.rs @@ -12,11 +12,11 @@ mod gas_hostfns { Self {} } - /// Checks that the host function `gas_limit` works - #[ink(message)] - pub fn gas_limit(&self) -> u64 { - self.env().gas_limit() - } + /// Checks that the host function `gas_limit` works + #[ink(message)] + pub fn gas_limit(&self) -> u64 { + self.env().gas_limit() + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -32,11 +32,7 @@ mod gas_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate( - "gas_hostfns", - &ink_e2e::alice(), - &mut GasHostfnsRef::new(), - ) + .instantiate("gas_hostfns", &ink_e2e::alice(), &mut GasHostfnsRef::new()) .submit() .await .expect("instantiate failed"); @@ -50,7 +46,7 @@ mod gas_hostfns { .unwrap_or_else(|err| { panic!("call failed: {:#?}", err); }); - + assert!(_call_res.return_value() > 0); Ok(()) From eb2f8a327f87433969dfcbe1343067035f46bd26 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 28 Oct 2025 12:07:05 -0300 Subject: [PATCH 08/36] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Müller --- crates/env/src/api.rs | 4 +++- crates/env/src/backend.rs | 2 +- crates/env/src/engine/off_chain/impls.rs | 3 +-- integration-tests/internal/gas-hostfns/lib.rs | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index a72868452bf..8490199e7dd 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -73,7 +73,9 @@ pub fn caller() -> Address { ::on_instance(TypedEnvBackend::caller) } -/// Returns the block ref_time limit. +/// Returns the block's `ref_time` limit. +/// +/// See for more information. pub fn gas_limit() -> u64 { ::on_instance(TypedEnvBackend::gas_limit) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 8735e3c5f05..3c02d58efb2 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -228,7 +228,7 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`caller`][`crate::caller`] fn caller(&mut self) -> Address; - /// Returns the block ref_time limit. + /// Returns the block's `ref_time` limit. /// /// # Note /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index cb88e8ca1b1..c7b3cfcc2ae 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -551,8 +551,7 @@ impl TypedEnvBackend for EnvInstance { } fn gas_limit(&mut self) -> u64 { - // Panic because of future depecration / removal - panic!(); + unimplemented!("not implemented, the off-chain environment will be removed"); } fn transferred_value(&mut self) -> U256 { diff --git a/integration-tests/internal/gas-hostfns/lib.rs b/integration-tests/internal/gas-hostfns/lib.rs index 88e46d5ec5d..8c5046541ac 100644 --- a/integration-tests/internal/gas-hostfns/lib.rs +++ b/integration-tests/internal/gas-hostfns/lib.rs @@ -39,7 +39,7 @@ mod gas_hostfns { let call_builder = contract.call_builder::(); // then - let _call_res = client + let call_res = client .call(&ink_e2e::alice(), &call_builder.gas_limit()) .submit() .await @@ -47,7 +47,7 @@ mod gas_hostfns { panic!("call failed: {:#?}", err); }); - assert!(_call_res.return_value() > 0); + assert!(call_res.return_value() > 0); Ok(()) } From 4a1f7e3cca03d017f640f1ba8ab8dacc94e43f48 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 28 Oct 2025 12:08:21 -0300 Subject: [PATCH 09/36] update versions in gas and misc hostfns internal tests --- integration-tests/internal/gas-hostfns/Cargo.toml | 2 +- integration-tests/internal/misc-hostfns/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/internal/gas-hostfns/Cargo.toml b/integration-tests/internal/gas-hostfns/Cargo.toml index 7ee2db9bc72..e85364d40ba 100755 --- a/integration-tests/internal/gas-hostfns/Cargo.toml +++ b/integration-tests/internal/gas-hostfns/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gas_hostfns" description = "E2E tests for gas related host functions" -version = "6.0.0-alpha.1" +version = "6.0.0-alpha.4" authors = ["Use Ink "] edition = "2021" publish = false diff --git a/integration-tests/internal/misc-hostfns/Cargo.toml b/integration-tests/internal/misc-hostfns/Cargo.toml index 27e7b955c88..8ec9020bdc9 100755 --- a/integration-tests/internal/misc-hostfns/Cargo.toml +++ b/integration-tests/internal/misc-hostfns/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "misc_hostfns" description = "E2E tests for various host functions" -version = "6.0.0-alpha.1" +version = "6.0.0-alpha.4" authors = ["Use Ink "] edition = "2021" publish = false From 560cf92929b6f526f36324b9bf33b16e625e09ea Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 18:40:03 -0300 Subject: [PATCH 10/36] impl and test `chain_id` hostfn --- crates/env/src/api.rs | 8 +++ crates/env/src/backend.rs | 8 +++ crates/env/src/engine/off_chain/impls.rs | 4 ++ .../env/src/engine/on_chain/pallet_revive.rs | 8 +++ crates/ink/src/env_access.rs | 34 +++++++++++ .../ink/tests/ui/contract/pass/env-access.rs | 2 + .../misc-evm-getters-hostfns/Cargo.toml | 27 +++++++++ .../internal/misc-evm-getters-hostfns/lib.rs | 57 +++++++++++++++++++ 8 files changed, 148 insertions(+) create mode 100755 integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml create mode 100644 integration-tests/internal/misc-evm-getters-hostfns/lib.rs diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index ad3be06aab8..fe8f0b9a035 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -111,6 +111,14 @@ pub fn return_data_size() -> u64 { ::on_instance(TypedEnvBackend::return_data_size) } +/// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, +/// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. +pub fn chain_id() -> U256 { + ::on_instance(|instance| { + TypedEnvBackend::chain_id(instance) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index ddb21119d72..db0a95da0a0 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -274,6 +274,14 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`return_data_size`][`crate::return_data_size] fn return_data_size(&mut self) -> u64; + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID. + /// This is akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. + /// + /// # Note + /// + /// For more details visit: [`chain_id`][`crate::chain_id] + fn chain_id(&mut self) -> U256; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 39385895596..24062cd9d28 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -564,6 +564,10 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn chain_id(&mut self) -> U256 { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn call_data_size(&mut self) -> u64 { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index ff2ede1acfe..547fbbd5ead 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -955,6 +955,14 @@ impl TypedEnvBackend for EnvInstance { ext::return_data_size() } + fn chain_id(&mut self) -> U256 { + let mut scope = self.scoped_buffer(); + let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + + ext::chain_id(u256); + U256::from_le_bytes(*u256) + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 3652e2d7630..3c15eda4906 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -210,6 +210,40 @@ where ink_env::gas_left() } + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, + /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_chain_id(&self) -> U256 { + /// self.env().chain_id() + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::chain_id`] + pub fn chain_id(self) -> U256 { + ink_env::chain_id() + } + /// Returns the total size of the contract call input data. /// This is akin to the EVM [CALLDATASIZE](https://www.evm.codes/?fork=cancun#36) opcode. /// diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index 0d515966a68..8f81ea6b854 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -17,6 +17,7 @@ mod contract { let _ = Self::env().gas_limit(); let _ = Self::env().gas_price(); let _ = Self::env().gas_left(); + let _ = Self::env().chain_id(); let _ = Self::env().call_data_size(); let _ = Self::env().return_data_size(); let _ = Self::env().transferred_value(); @@ -35,6 +36,7 @@ mod contract { let _ = self.env().gas_limit(); let _ = self.env().gas_price(); let _ = self.env().gas_left(); + let _ = self.env().chain_id(); let _ = self.env().call_data_size(); let _ = self.env().return_data_size(); let _ = self.env().transferred_value(); diff --git a/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml b/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml new file mode 100755 index 00000000000..9e69e2a5007 --- /dev/null +++ b/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "misc-evm-getters-hostfns" +description = "E2E tests for evm getters related host functions" +version = "6.0.0-beta" +authors = ["Use Ink "] +edition = "2021" +publish = false + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false } + +[dev-dependencies] +ink_e2e = { path = "../../../crates/e2e" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", +] +ink-as-dependency = [] +e2e-tests = [] + +[package.metadata.ink-lang] +abi = "ink" diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs new file mode 100644 index 00000000000..69ea81621d9 --- /dev/null +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -0,0 +1,57 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] +#![allow(clippy::new_without_default)] + +#[ink::contract] +mod misc_evm_getters_hostfns { + use ink::U256; + + #[ink(storage)] + pub struct MiscEVMGettersfns {} + + impl MiscEVMGettersfns { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + /// Checks that the host function `chain_id` works + #[ink(message)] + pub fn chain_id(&self) -> U256 { + self.env().chain_id() + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn e2e_chain_id_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let call_res = client + .call(&ink_e2e::alice(), &call_builder.chain_id()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + assert!(call_res.return_value() > U256::from(0)); + + Ok(()) + } + } +} From aced7283019120397d40e9c22b0657cd554d38e3 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 18:44:13 -0300 Subject: [PATCH 11/36] add entry to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 829c2180cef..849c41839c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased] ### Changed +- Implements the API for the `pallet-revive` host functions `chain_id`, `balance_of`, `base_fee`, `origin`, `code_size`, `block_hash`, `block_author` - [#2719](https://github.com/use-ink/ink/pull/2719) - Refactor contract ref generation and add automatic re-exporting - [#2710](https://github.com/use-ink/ink/pull/2710) - Implement and stabilize `terminate_contract` ‒ [2708](https://github.com/use-ink/ink/pull/2708) From b970f07c5cf1a06d535ca81ae95d289fa66bfa67 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 19:21:27 -0300 Subject: [PATCH 12/36] impl and test `balance_of` host fn --- crates/env/src/api.rs | 8 ++ crates/env/src/backend.rs | 10 ++- crates/env/src/engine/off_chain/impls.rs | 4 + .../env/src/engine/on_chain/pallet_revive.rs | 10 +++ crates/ink/src/env_access.rs | 79 +++++++++++++------ .../internal/misc-evm-getters-hostfns/lib.rs | 33 ++++++++ 6 files changed, 121 insertions(+), 23 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index fe8f0b9a035..7068b1b045c 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -119,6 +119,14 @@ pub fn chain_id() -> U256 { }) } +/// Returns the **reducible** native balance of the supplied address, +// akin to the EVM [BALANCE](https://www.evm.codes/?fork=cancun#31) opcode. +pub fn balance_of(addr: Address) -> U256 { + ::on_instance(|instance| { + TypedEnvBackend::balance_of(instance, addr) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index db0a95da0a0..03930520b4c 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -271,7 +271,7 @@ pub trait TypedEnvBackend: EnvBackend { /// /// # Note /// - /// For more details visit: [`return_data_size`][`crate::return_data_size] + /// For more details visit: [`return_data_size`][`crate::return_data_size`] fn return_data_size(&mut self) -> u64; /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID. @@ -282,6 +282,14 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`chain_id`][`crate::chain_id] fn chain_id(&mut self) -> U256; + /// Returns the **reducible** native balance of the supplied address. + /// This is akin to the EVM [BALANCE](https://www.evm.codes/?fork=cancun#31) opcode. + /// + /// # Note + /// + /// For more details visit: [`balance_of`][`crate::balance_of`] + fn balance_of(&mut self, addr: Address) -> U256; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 24062cd9d28..f8eda7f7568 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -568,6 +568,10 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn balance_of(&mut self, _addr: Address) -> U256 { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn call_data_size(&mut self) -> u64 { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 547fbbd5ead..4e7cf3d87aa 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -963,6 +963,16 @@ impl TypedEnvBackend for EnvInstance { U256::from_le_bytes(*u256) } + fn balance_of(&mut self, addr: Address) -> U256 { + let mut scope = self.scoped_buffer(); + let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + + let addr = addr.as_fixed_bytes(); + + ext::balance_of(&addr, u256); + U256::from_le_bytes(*u256) + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 3c15eda4906..71a6596abed 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -210,16 +210,46 @@ where ink_env::gas_left() } - /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, - /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. + /// Returns the total size of the contract call input data. + /// This is akin to the EVM [CALLDATASIZE](https://www.evm.codes/?fork=cancun#36) opcode. /// /// # Example /// /// ``` /// #[ink::contract] /// mod my_contract { - /// use ink::U256; + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_call_data_size(&self) -> u64 { + /// self.env().call_data_size() + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::call_data_size`] + pub fn call_data_size(self) -> u64 { + ink_env::call_data_size() + } + + /// Returns the size of the returned data of the last contract call or instantiation. + /// This is akin to the EVM [RETURNDATASIZE](https://www.evm.codes/?fork=cancun#3d) opcode. /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { /// #[ink(storage)] /// pub struct MyContract; /// @@ -230,8 +260,8 @@ where /// } /// /// #[ink(message)] - /// pub fn get_chain_id(&self) -> U256 { - /// self.env().chain_id() + /// pub fn get_return_data_size(&self) -> u64 { + /// self.env().return_data_size() /// } /// } /// } @@ -239,19 +269,21 @@ where /// /// # Note /// - /// For more details visit: [`ink_env::chain_id`] - pub fn chain_id(self) -> U256 { - ink_env::chain_id() + /// For more details visit: [`ink_env::return_data_size`] + pub fn return_data_size(self) -> u64 { + ink_env::return_data_size() } - /// Returns the total size of the contract call input data. - /// This is akin to the EVM [CALLDATASIZE](https://www.evm.codes/?fork=cancun#36) opcode. + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, + /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. /// /// # Example /// /// ``` /// #[ink::contract] /// mod my_contract { + /// use ink::U256; + /// /// #[ink(storage)] /// pub struct MyContract; /// @@ -262,8 +294,8 @@ where /// } /// /// #[ink(message)] - /// pub fn get_call_data_size(&self) -> u64 { - /// self.env().call_data_size() + /// pub fn get_chain_id(&self) -> U256 { + /// self.env().chain_id() /// } /// } /// } @@ -271,19 +303,21 @@ where /// /// # Note /// - /// For more details visit: [`ink_env::call_data_size`] - pub fn call_data_size(self) -> u64 { - ink_env::call_data_size() + /// For more details visit: [`ink_env::chain_id`] + pub fn chain_id(self) -> U256 { + ink_env::chain_id() } - /// Returns the size of the returned data of the last contract call or instantiation. - /// This is akin to the EVM [RETURNDATASIZE](https://www.evm.codes/?fork=cancun#3d) opcode. + /// Returns the **reducible** native balance of the supplied address. + /// This is akin to the EVM [BALANCE](https://www.evm.codes/?fork=cancun#31) opcode. /// /// # Example /// /// ``` /// #[ink::contract] /// mod my_contract { + /// use ink::U256; + /// /// #[ink(storage)] /// pub struct MyContract; /// @@ -294,8 +328,9 @@ where /// } /// /// #[ink(message)] - /// pub fn get_return_data_size(&self) -> u64 { - /// self.env().return_data_size() + /// pub fn get_my_balance(&self) -> U256 { + /// let caller = self.env().caller(); + /// self.env().balance_of(caller) /// } /// } /// } @@ -303,9 +338,9 @@ where /// /// # Note /// - /// For more details visit: [`ink_env::return_data_size`] - pub fn return_data_size(self) -> u64 { - ink_env::return_data_size() + /// For more details visit: [`ink_env::balance_of`] + pub fn balance_of(self, addr: Address) -> U256 { + ink_env::balance_of(addr) } /// Returns the transferred value for the contract execution. diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index 69ea81621d9..d71a399f557 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -19,6 +19,13 @@ mod misc_evm_getters_hostfns { pub fn chain_id(&self) -> U256 { self.env().chain_id() } + + /// Checks that the host function `balance_of` works + #[ink(message)] + pub fn balance_of(&self) -> U256 { + let caller = self.env().caller(); + self.env().balance_of(caller) + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -53,5 +60,31 @@ mod misc_evm_getters_hostfns { Ok(()) } + + #[ink_e2e::test] + async fn e2e_balance_of_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let call_res = client + .call(&ink_e2e::alice(), &call_builder.balance_of()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + assert!(call_res.return_value() > U256::from(0)); + + Ok(()) + } } } From 8e5e5eb9c289d5979ba10626ef569128c9bb64d4 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 19:29:11 -0300 Subject: [PATCH 13/36] impl and test `base_fee` --- crates/env/src/api.rs | 10 +++++- crates/env/src/backend.rs | 8 +++++ crates/env/src/engine/off_chain/impls.rs | 4 +++ .../env/src/engine/on_chain/pallet_revive.rs | 8 +++++ crates/ink/src/env_access.rs | 35 +++++++++++++++++++ .../ink/tests/ui/contract/pass/env-access.rs | 2 ++ .../internal/misc-evm-getters-hostfns/lib.rs | 32 +++++++++++++++++ 7 files changed, 98 insertions(+), 1 deletion(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 7068b1b045c..a47384afe18 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -120,13 +120,21 @@ pub fn chain_id() -> U256 { } /// Returns the **reducible** native balance of the supplied address, -// akin to the EVM [BALANCE](https://www.evm.codes/?fork=cancun#31) opcode. +/// akin to the EVM [BALANCE](https://www.evm.codes/?fork=cancun#31) opcode. pub fn balance_of(addr: Address) -> U256 { ::on_instance(|instance| { TypedEnvBackend::balance_of(instance, addr) }) } +/// Returns the base fee. +/// This is akin to the EVM [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. +pub fn base_fee() -> u256 { + ::on_instance(|instance| { + TypedEnvBackend::base_fee(instance) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 03930520b4c..8880626c5f5 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -290,6 +290,14 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`balance_of`][`crate::balance_of`] fn balance_of(&mut self, addr: Address) -> U256; + /// Returns the base fee. + /// This is akin to the EVM [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. + /// + /// # Note + /// + /// For more details visit: [`base_fee`][`crate::base_fee`] + fn base_fee(&mut self) -> u256; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index f8eda7f7568..b9694877263 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -572,6 +572,10 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn base_fee(&mut self) -> U256 { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn call_data_size(&mut self) -> u64 { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 4e7cf3d87aa..6152ec081e8 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -973,6 +973,14 @@ impl TypedEnvBackend for EnvInstance { U256::from_le_bytes(*u256) } + fn base_fee(&mut self) -> U256 { + let mut scope = self.scoped_buffer(); + let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + + ext::base_fee(u256); + U256::from_le_bytes(*u256) + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 71a6596abed..fef216a0dce 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -343,6 +343,41 @@ where ink_env::balance_of(addr) } + + /// Returns the base fee. + /// This is akin to the EVM [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_base_fee(&self) -> U256 { + /// self.env().base_fee() + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::base_fee`] + pub fn base_fee(self) -> U256 { + ink_env::base_fee() + } + /// Returns the transferred value for the contract execution. /// /// # Example diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index 8f81ea6b854..79db3888b5c 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -20,6 +20,7 @@ mod contract { let _ = Self::env().chain_id(); let _ = Self::env().call_data_size(); let _ = Self::env().return_data_size(); + let _ = Self::env().base_fee(); let _ = Self::env().transferred_value(); let _ = Self::env().weight_to_fee(0); Self {} @@ -39,6 +40,7 @@ mod contract { let _ = self.env().chain_id(); let _ = self.env().call_data_size(); let _ = self.env().return_data_size(); + let _ = self.env().base_fee(); let _ = self.env().transferred_value(); let _ = self.env().weight_to_fee(0); } diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index d71a399f557..d1b3cca053c 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -26,6 +26,12 @@ mod misc_evm_getters_hostfns { let caller = self.env().caller(); self.env().balance_of(caller) } + + /// Checks that the host function `base_fee` works + #[ink(message)] + pub fn base_fee(&self) -> U256 { + self.env().base_fee() + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -86,5 +92,31 @@ mod misc_evm_getters_hostfns { Ok(()) } + + #[ink_e2e::test] + async fn e2e_base_fee_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let call_res = client + .call(&ink_e2e::alice(), &call_builder.base_fee()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + assert!(call_res.return_value() > U256::from(0)); + + Ok(()) + } } } From efaa0b8f8e66cb7d5436ac1eacd503cf2f7b32bc Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 19:50:29 -0300 Subject: [PATCH 14/36] impl and test `origin` --- crates/env/src/api.rs | 14 +++++++- crates/env/src/backend.rs | 14 +++++++- crates/env/src/engine/off_chain/impls.rs | 4 +++ .../env/src/engine/on_chain/pallet_revive.rs | 8 +++++ crates/ink/src/env_access.rs | 32 +++++++++++++++++ .../ink/tests/ui/contract/pass/env-access.rs | 2 ++ .../internal/misc-evm-getters-hostfns/lib.rs | 34 ++++++++++++++++++- 7 files changed, 105 insertions(+), 3 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index a47384afe18..79ec5e402a7 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -129,12 +129,24 @@ pub fn balance_of(addr: Address) -> U256 { /// Returns the base fee. /// This is akin to the EVM [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. -pub fn base_fee() -> u256 { +pub fn base_fee() -> U256 { ::on_instance(|instance| { TypedEnvBackend::base_fee(instance) }) } +/// Returns the origin address (initator of the call stack). +/// This is akin to the EVM [ORIGIN](https://www.evm.codes/?fork=cancun#32) opcode. +/// +/// # Errors +/// +/// - If there is no address associated with the origin (e.g. because the origin is root). +pub fn origin() -> Address { + ::on_instance(|instance| { + TypedEnvBackend::origin(instance) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 8880626c5f5..30080f12b6e 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -296,7 +296,19 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`base_fee`][`crate::base_fee`] - fn base_fee(&mut self) -> u256; + fn base_fee(&mut self) -> U256; + + /// Returns the origin address (initator of the call stack). + /// This is akin to the EVM [ORIGIN](https://www.evm.codes/?fork=cancun#32) opcode. + /// + /// # Errors + /// + /// - If there is no address associated with the origin (e.g. because the origin is root). + /// + /// # Note + /// + /// For more details visit: [`origin`][`crate::origin`] + fn origin(&mut self) -> Address; /// Returns the transferred value for the contract execution. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index b9694877263..ea0b51d023f 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -576,6 +576,10 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn origin(&mut self) -> Address { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn call_data_size(&mut self) -> u64 { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 6152ec081e8..07e98c02a28 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -981,6 +981,14 @@ impl TypedEnvBackend for EnvInstance { U256::from_le_bytes(*u256) } + fn origin(&mut self) -> Address { + let mut scope = self.scoped_buffer(); + let h160: &mut [u8; 20] = scope.take(20).try_into().unwrap(); + + ext::origin(h160); + h160.into() + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index fef216a0dce..db6bbbc81eb 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -378,6 +378,38 @@ where ink_env::base_fee() } + /// Returns the origin address (initator of the call stack). + /// This is akin to the EVM [ORIGIN](https://www.evm.codes/?fork=cancun#32) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_origin(&self) -> Address { + /// self.env().origin() + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::origin`] + pub fn origin(self) -> Address { + ink_env::origin() + } + /// Returns the transferred value for the contract execution. /// /// # Example diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index 79db3888b5c..f6b4837a19e 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -21,6 +21,7 @@ mod contract { let _ = Self::env().call_data_size(); let _ = Self::env().return_data_size(); let _ = Self::env().base_fee(); + let _ = Self::env().origin(); let _ = Self::env().transferred_value(); let _ = Self::env().weight_to_fee(0); Self {} @@ -41,6 +42,7 @@ mod contract { let _ = self.env().call_data_size(); let _ = self.env().return_data_size(); let _ = self.env().base_fee(); + let _ = self.env().origin(); let _ = self.env().transferred_value(); let _ = self.env().weight_to_fee(0); } diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index d1b3cca053c..9af852a1647 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -32,12 +32,18 @@ mod misc_evm_getters_hostfns { pub fn base_fee(&self) -> U256 { self.env().base_fee() } + + /// Checks that the host function `origin` works + #[ink(message)] + pub fn origin(&self) -> Address { + self.env().origin() + } } #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; - use ink_e2e::ContractsBackend; + use ink_e2e::{ContractsBackend,address_from_keypair}; type E2EResult = std::result::Result>; @@ -118,5 +124,31 @@ mod misc_evm_getters_hostfns { Ok(()) } + + #[ink_e2e::test] + async fn e2e_origin_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let call_res = client + .call(&ink_e2e::alice(), &call_builder.origin()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + assert_eq!(call_res.return_value(), address_from_keypair::(&ink_e2e::alice())); + + Ok(()) + } } } From 071db36cc70cdf933ca9edefb3c500f3587f3ecd Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 20:02:17 -0300 Subject: [PATCH 15/36] impl and test `code_size` hostfn --- crates/env/src/api.rs | 12 +++++++ crates/env/src/backend.rs | 10 ++++++ crates/env/src/engine/off_chain/impls.rs | 4 +++ .../env/src/engine/on_chain/pallet_revive.rs | 6 ++++ crates/ink/src/env_access.rs | 34 +++++++++++++++++++ .../internal/misc-evm-getters-hostfns/lib.rs | 33 ++++++++++++++++++ 6 files changed, 99 insertions(+) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 79ec5e402a7..05ea1dc0f4c 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -147,6 +147,18 @@ pub fn origin() -> Address { }) } +/// Returns the code size for a specified contract address. +/// This is akin to the EVM [CODESIZE](https://www.evm.codes/?fork=cancun#38) opcode. +/// +/// # Note +/// +/// If `addr` is not a contract the `output` will be zero. +pub fn code_size(addr: Address) -> u64 { + ::on_instance(|instance| { + TypedEnvBackend::code_size(instance, addr) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 30080f12b6e..c51c5192c31 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -310,6 +310,16 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`origin`][`crate::origin`] fn origin(&mut self) -> Address; + /// Returns the code size for a specified contract address. + /// This is akin to the EVM [CODESIZE](https://www.evm.codes/?fork=cancun#38) opcode. + /// + /// # Note + /// + /// If `addr` is not a contract the `output` will be zero. + /// For more details visit: [`code_size`][`crate::code_size`] + fn code_size(&mut self, addr: Address) -> u64; + + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index ea0b51d023f..5af2f3e5fad 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -579,6 +579,10 @@ impl TypedEnvBackend for EnvInstance { fn origin(&mut self) -> Address { unimplemented!("not implemented, the off-chain environment will be removed"); } + + fn code_size(&mut self, _addr: Address) -> u64 { + unimplemented!("not implemented, the off-chain environment will be removed"); + } fn call_data_size(&mut self) -> u64 { unimplemented!("not implemented, the off-chain environment will be removed"); diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 07e98c02a28..c7a1082b3d6 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -989,6 +989,12 @@ impl TypedEnvBackend for EnvInstance { h160.into() } + fn code_size(&mut self, addr: Address) -> u64 { + let addr = addr.as_fixed_bytes(); + + ext::code_size(addr) + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index db6bbbc81eb..4ca3406aaea 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -410,6 +410,40 @@ where ink_env::origin() } + /// Returns the code size for a specified contract address. + /// This is akin to the EVM [CODESIZE](https://www.evm.codes/?fork=cancun#38) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_code_size_of_this_contract(&self) -> u64 { + /// let this_addr = self.env().address(); + /// self.env().code_size(this_addr) + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// If `addr` is not a contract the `output` will be zero. + /// For more details visit: [`ink_env::code_size`] + pub fn code_size(self, addr: Address) -> u64 { + ink_env::code_size(addr) + } + /// Returns the transferred value for the contract execution. /// /// # Example diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index 9af852a1647..f6194bb015e 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -38,6 +38,13 @@ mod misc_evm_getters_hostfns { pub fn origin(&self) -> Address { self.env().origin() } + + /// Checks that the host function `code_size` works + #[ink(message)] + pub fn code_size(&self) -> u64 { + let this_addr = self.env().address(); + self.env().code_size(this_addr) + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -148,6 +155,32 @@ mod misc_evm_getters_hostfns { assert_eq!(call_res.return_value(), address_from_keypair::(&ink_e2e::alice())); + Ok(()) + } + + #[ink_e2e::test] + async fn e2e_code_size_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let call_res = client + .call(&ink_e2e::alice(), &call_builder.code_size()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + assert!(call_res.return_value() > 0); + Ok(()) } } From 64c31f986e96c92fbbe852f89236e805498b5bae Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 20:21:29 -0300 Subject: [PATCH 16/36] impl and test `block_author` --- crates/env/src/api.rs | 8 +++++ crates/env/src/backend.rs | 8 +++++ crates/env/src/engine/off_chain/impls.rs | 6 +++- .../env/src/engine/on_chain/pallet_revive.rs | 12 ++++++- crates/ink/src/env_access.rs | 32 +++++++++++++++++++ .../ink/tests/ui/contract/pass/env-access.rs | 2 ++ .../internal/misc-evm-getters-hostfns/lib.rs | 32 ++++++++++++++++++- 7 files changed, 97 insertions(+), 3 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 05ea1dc0f4c..4686d449986 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -159,6 +159,14 @@ pub fn code_size(addr: Address) -> u64 { }) } +/// Returns the current block author. +/// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. +pub fn block_author() -> Address { + ::on_instance(|instance| { + TypedEnvBackend::block_author(instance) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index c51c5192c31..4bd6c51af7a 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -319,6 +319,14 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`code_size`][`crate::code_size`] fn code_size(&mut self, addr: Address) -> u64; + /// Returns the current block author. + /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. + /// + /// # Note + /// + /// For more details visit: [`block_author`][`crate::block_author`] + fn block_author(&mut self) -> Address; + /// Returns the transferred value for the contract execution. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 5af2f3e5fad..71eadb3dafa 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -579,7 +579,11 @@ impl TypedEnvBackend for EnvInstance { fn origin(&mut self) -> Address { unimplemented!("not implemented, the off-chain environment will be removed"); } - + + fn block_author(&mut self) -> Address { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn code_size(&mut self, _addr: Address) -> u64 { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index c7a1082b3d6..805848731f7 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -991,10 +991,20 @@ impl TypedEnvBackend for EnvInstance { fn code_size(&mut self, addr: Address) -> u64 { let addr = addr.as_fixed_bytes(); - + ext::code_size(addr) } + fn block_author(&mut self) -> Address { + let h160 = { + let mut scope = self.scoped_buffer(); + let h160: &mut [u8; 20] = scope.take(20).try_into().unwrap(); + ext::block_author(h160); + *h160 + }; + h160.into() + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 4ca3406aaea..6f1acc31f56 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -444,6 +444,38 @@ where ink_env::code_size(addr) } + /// Returns the current block author. + /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_block_author(&self) -> Address { + /// self.env().block_author() + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::block_author`] + pub fn block_author(self) -> Address { + ink_env::block_author() + } + /// Returns the transferred value for the contract execution. /// /// # Example diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index f6b4837a19e..672443cff6e 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -22,6 +22,7 @@ mod contract { let _ = Self::env().return_data_size(); let _ = Self::env().base_fee(); let _ = Self::env().origin(); + let _ = Self::env().block_author(); let _ = Self::env().transferred_value(); let _ = Self::env().weight_to_fee(0); Self {} @@ -43,6 +44,7 @@ mod contract { let _ = self.env().return_data_size(); let _ = self.env().base_fee(); let _ = self.env().origin(); + let _ = self.env().block_author(); let _ = self.env().transferred_value(); let _ = self.env().weight_to_fee(0); } diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index f6194bb015e..b6144712f4c 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -45,6 +45,12 @@ mod misc_evm_getters_hostfns { let this_addr = self.env().address(); self.env().code_size(this_addr) } + + /// Checks that the host function `block_author` works + #[ink(message)] + pub fn block_author(&self) -> Address { + self.env().block_author() + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -158,7 +164,7 @@ mod misc_evm_getters_hostfns { Ok(()) } - #[ink_e2e::test] + #[ink_e2e::test] async fn e2e_code_size_works( mut client: Client, ) -> E2EResult<()> { @@ -183,5 +189,29 @@ mod misc_evm_getters_hostfns { Ok(()) } + + #[ink_e2e::test] + async fn e2e_block_author_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let _call_res = client + .call(&ink_e2e::alice(), &call_builder.block_author()) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + Ok(()) + } } } From 2b92a3ffcedc515c946c71b8971f9375397a98f8 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 20:47:56 -0300 Subject: [PATCH 17/36] impl and test `block_hash` --- crates/env/src/api.rs | 9 +++++ crates/env/src/backend.rs | 9 +++++ crates/env/src/engine/off_chain/impls.rs | 5 +++ .../env/src/engine/on_chain/pallet_revive.rs | 15 +++++++++ crates/ink/src/env_access.rs | 33 +++++++++++++++++++ .../misc-evm-getters-hostfns/Cargo.toml | 1 + .../internal/misc-evm-getters-hostfns/lib.rs | 32 +++++++++++++++++- 7 files changed, 103 insertions(+), 1 deletion(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 4686d449986..30d8624cd82 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -20,6 +20,7 @@ use ink_primitives::{ Address, CodeHashErr, H256, + types::BlockNumber, U256, abi::{ Ink, @@ -159,6 +160,14 @@ pub fn code_size(addr: Address) -> u64 { }) } +/// Returns the block hash of the given block number. +/// This is akin to the EVM [BLOCKHASH](https://www.evm.codes/?fork=cancun#40) opcode. +pub fn block_hash(block_number: BlockNumber) -> H256 { + ::on_instance(|instance| { + TypedEnvBackend::block_hash(instance, block_number) + }) +} + /// Returns the current block author. /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. pub fn block_author() -> Address { diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 4bd6c51af7a..1a0efe2c3ae 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -18,6 +18,7 @@ use ink_primitives::{ Address, CodeHashErr, H256, + types::BlockNumber, U256, sol::SolResultEncode, types::Environment, @@ -319,6 +320,14 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`code_size`][`crate::code_size`] fn code_size(&mut self, addr: Address) -> u64; + /// Returns the block hash of the given block number. + /// This is akin to the EVM [BLOCKHASH](https://www.evm.codes/?fork=cancun#40) opcode. + /// + /// # Note + /// + /// For more details visit: [`block_hash`][`crate::block_hash`] + fn block_hash(&mut self, block_number: BlockNumber) -> H256; + /// Returns the current block author. /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 71eadb3dafa..153e9e47bbf 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -19,6 +19,7 @@ use ink_primitives::{ Address, CodeHashErr, H256, + types::BlockNumber, U256, abi::{ AbiEncodeWith, @@ -580,6 +581,10 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn block_hash(&mut self, block_number: BlockNumber) -> H256 { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn block_author(&mut self) -> Address { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 805848731f7..3c5b1bcfe8f 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -18,6 +18,7 @@ use ink_primitives::{ Address, CodeHashErr, H256, + types::BlockNumber, U256, abi::{ AbiEncodeWith, @@ -995,6 +996,20 @@ impl TypedEnvBackend for EnvInstance { ext::code_size(addr) } + fn block_hash(&mut self, block_number: BlockNumber) -> H256 { + let mut scope = self.scoped_buffer(); + let output: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + + let block_number = { + let mut bytes = [0u8; 32]; + bytes[..4].copy_from_slice(&block_number.to_le_bytes()); + bytes + }; + + ext::block_hash(&block_number, output); + H256::from_slice(output) + } + fn block_author(&mut self) -> Address { let h160 = { let mut scope = self.scoped_buffer(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 6f1acc31f56..7e1f2c4dbcc 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -41,6 +41,7 @@ use ink_primitives::{ Address, CodeHashErr, H256, + types::BlockNumber, U256, abi::{ Ink, @@ -444,6 +445,38 @@ where ink_env::code_size(addr) } + /// Returns the block hash of the given block number. + /// This is akin to the EVM [BLOCKHASH](https://www.evm.codes/?fork=cancun#40) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_block_hash(&self, block_number: BlockNumber) -> H256 { + /// self.env().block_hash(block_number) + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::block_hash`] + pub fn block_hash(self, block_number: BlockNumber) -> H256 { + ink_env::block_hash(block_number) + } + /// Returns the current block author. /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. /// diff --git a/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml b/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml index 9e69e2a5007..0bca3e10d11 100755 --- a/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml +++ b/integration-tests/internal/misc-evm-getters-hostfns/Cargo.toml @@ -11,6 +11,7 @@ ink = { path = "../../../crates/ink", default-features = false } [dev-dependencies] ink_e2e = { path = "../../../crates/e2e" } +ink_primitives = { path = "../../../crates/primitives" } [lib] path = "lib.rs" diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index b6144712f4c..203ad6a78c2 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -3,7 +3,7 @@ #[ink::contract] mod misc_evm_getters_hostfns { - use ink::U256; + use ink::{U256, H256}; #[ink(storage)] pub struct MiscEVMGettersfns {} @@ -46,6 +46,12 @@ mod misc_evm_getters_hostfns { self.env().code_size(this_addr) } + /// Checks that the host function `block_hash` works + #[ink(message)] + pub fn block_hash(&self, block_number: BlockNumber) -> H256 { + self.env().block_hash(block_number) + } + /// Checks that the host function `block_author` works #[ink(message)] pub fn block_author(&self) -> Address { @@ -190,6 +196,30 @@ mod misc_evm_getters_hostfns { Ok(()) } + #[ink_e2e::test] + async fn e2e_block_hash_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let _call_res = client + .call(&ink_e2e::alice(), &call_builder.block_hash(BlockNumber::from(0u32))) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + Ok(()) + } + #[ink_e2e::test] async fn e2e_block_author_works( mut client: Client, From 28e25f9e9967603cfef7749aec09bf45eca66539 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 9 Nov 2025 20:49:56 -0300 Subject: [PATCH 18/36] run formatting --- crates/env/src/api.rs | 6 +- crates/env/src/backend.rs | 10 +-- crates/env/src/engine/off_chain/impls.rs | 2 +- .../env/src/engine/on_chain/pallet_revive.rs | 2 +- crates/ink/src/env_access.rs | 3 +- .../internal/misc-evm-getters-hostfns/lib.rs | 62 +++++++++++++++---- 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 30d8624cd82..2aeb39e918e 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -20,13 +20,13 @@ use ink_primitives::{ Address, CodeHashErr, H256, - types::BlockNumber, U256, abi::{ Ink, Sol, }, sol::SolResultEncode, + types::BlockNumber, }; use ink_storage_traits::Storable; use pallet_revive_uapi::ReturnFlags; @@ -143,9 +143,7 @@ pub fn base_fee() -> U256 { /// /// - If there is no address associated with the origin (e.g. because the origin is root). pub fn origin() -> Address { - ::on_instance(|instance| { - TypedEnvBackend::origin(instance) - }) + ::on_instance(|instance| TypedEnvBackend::origin(instance)) } /// Returns the code size for a specified contract address. diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 1a0efe2c3ae..f719dc66d70 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -18,10 +18,12 @@ use ink_primitives::{ Address, CodeHashErr, H256, - types::BlockNumber, U256, sol::SolResultEncode, - types::Environment, + types::{ + BlockNumber, + Environment, + }, }; use ink_storage_traits::Storable; pub use pallet_revive_uapi::ReturnFlags; @@ -304,7 +306,8 @@ pub trait TypedEnvBackend: EnvBackend { /// /// # Errors /// - /// - If there is no address associated with the origin (e.g. because the origin is root). + /// - If there is no address associated with the origin (e.g. because the origin is + /// root). /// /// # Note /// @@ -336,7 +339,6 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`block_author`][`crate::block_author`] fn block_author(&mut self) -> Address; - /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 153e9e47bbf..830e388c972 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -19,7 +19,6 @@ use ink_primitives::{ Address, CodeHashErr, H256, - types::BlockNumber, U256, abi::{ AbiEncodeWith, @@ -29,6 +28,7 @@ use ink_primitives::{ sol::SolResultEncode, types::{ AccountIdMapper, + BlockNumber, Environment, }, }; diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 3c5b1bcfe8f..600a9bcdbbd 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -18,7 +18,6 @@ use ink_primitives::{ Address, CodeHashErr, H256, - types::BlockNumber, U256, abi::{ AbiEncodeWith, @@ -26,6 +25,7 @@ use ink_primitives::{ Sol, }, sol::SolResultEncode, + types::BlockNumber, }; use ink_storage_traits::{ Storable, diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 7e1f2c4dbcc..227f33d6297 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -41,12 +41,12 @@ use ink_primitives::{ Address, CodeHashErr, H256, - types::BlockNumber, U256, abi::{ Ink, Sol, }, + types::BlockNumber, }; use pallet_revive_uapi::ReturnErrorCode; @@ -344,7 +344,6 @@ where ink_env::balance_of(addr) } - /// Returns the base fee. /// This is akin to the EVM [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. /// diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index 203ad6a78c2..24109cbb7bb 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -3,7 +3,10 @@ #[ink::contract] mod misc_evm_getters_hostfns { - use ink::{U256, H256}; + use ink::{ + H256, + U256, + }; #[ink(storage)] pub struct MiscEVMGettersfns {} @@ -62,7 +65,10 @@ mod misc_evm_getters_hostfns { #[cfg(all(test, feature = "e2e-tests"))] mod e2e_tests { use super::*; - use ink_e2e::{ContractsBackend,address_from_keypair}; + use ink_e2e::{ + ContractsBackend, + address_from_keypair, + }; type E2EResult = std::result::Result>; @@ -72,7 +78,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); @@ -98,7 +108,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); @@ -124,7 +138,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); @@ -150,7 +168,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); @@ -165,7 +187,10 @@ mod misc_evm_getters_hostfns { panic!("call failed: {:#?}", err); }); - assert_eq!(call_res.return_value(), address_from_keypair::(&ink_e2e::alice())); + assert_eq!( + call_res.return_value(), + address_from_keypair::(&ink_e2e::alice()) + ); Ok(()) } @@ -176,7 +201,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); @@ -202,7 +231,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); @@ -210,7 +243,10 @@ mod misc_evm_getters_hostfns { // then let _call_res = client - .call(&ink_e2e::alice(), &call_builder.block_hash(BlockNumber::from(0u32))) + .call( + &ink_e2e::alice(), + &call_builder.block_hash(BlockNumber::from(0u32)), + ) .submit() .await .unwrap_or_else(|err| { @@ -226,7 +262,11 @@ mod misc_evm_getters_hostfns { ) -> E2EResult<()> { // given let contract = client - .instantiate("misc_evm_getters_hostfns", &ink_e2e::alice(), &mut MiscEVMGettersfnsRef::new()) + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) .submit() .await .expect("instantiate failed"); From ca4eb06848e27b1f94188a09cc5252b68636d6fb Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Mon, 10 Nov 2025 11:59:12 -0300 Subject: [PATCH 19/36] fix some ci/cd issues --- crates/env/src/api.rs | 14 ++++---------- crates/env/src/engine/off_chain/impls.rs | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index c855f8e6404..758771549ee 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -115,9 +115,7 @@ pub fn return_data_size() -> u64 { /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. pub fn chain_id() -> U256 { - ::on_instance(|instance| { - TypedEnvBackend::chain_id(instance) - }) + ::on_instance(TypedEnvBackend::chain_id) } /// Returns the **reducible** native balance of the supplied address, @@ -131,9 +129,7 @@ pub fn balance_of(addr: Address) -> U256 { /// Returns the base fee. /// This is akin to the EVM [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. pub fn base_fee() -> U256 { - ::on_instance(|instance| { - TypedEnvBackend::base_fee(instance) - }) + ::on_instance(TypedEnvBackend::base_fee) } /// Returns the origin address (initator of the call stack). @@ -143,7 +139,7 @@ pub fn base_fee() -> U256 { /// /// - If there is no address associated with the origin (e.g. because the origin is root). pub fn origin() -> Address { - ::on_instance(|instance| TypedEnvBackend::origin(instance)) + ::on_instance(TypedEnvBackend::origin) } /// Returns the code size for a specified contract address. @@ -169,9 +165,7 @@ pub fn block_hash(block_number: BlockNumber) -> H256 { /// Returns the current block author. /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. pub fn block_author() -> Address { - ::on_instance(|instance| { - TypedEnvBackend::block_author(instance) - }) + ::on_instance(TypedEnvBackend::block_author) } /// Returns the transferred value for the contract execution. diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 2fe209c6d3e..5e042398760 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -599,7 +599,7 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } - fn block_hash(&mut self, block_number: BlockNumber) -> H256 { + fn block_hash(&mut self, _block_number: BlockNumber) -> H256 { unimplemented!("not implemented, the off-chain environment will be removed"); } From d9b2e3d36861d3c922d3045d0c7ed7c7ab937181 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Mon, 10 Nov 2025 15:20:45 -0300 Subject: [PATCH 20/36] fix error[E0412]: cannot find type in this scope --- crates/ink/src/env_access.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index b304f2ae5a6..bed1943d928 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -472,7 +472,7 @@ where /// # Note /// /// For more details visit: [`ink_env::block_hash`] - pub fn block_hash(self, block_number: BlockNumber) -> H256 { + pub fn block_hash(self, block_number: BlockNumber) -> ink_primitives::H256 { ink_env::block_hash(block_number) } From a0cd8c506ecaa2873e3ac7bf0856efd986f74951 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 11 Nov 2025 11:40:12 -0300 Subject: [PATCH 21/36] fix docs for chain_id --- crates/env/src/backend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 70810d38ba2..b5794fe343f 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -315,7 +315,7 @@ pub trait TypedEnvBackend: EnvBackend { /// /// # Note /// - /// For more details visit: [`chain_id`][`crate::chain_id] + /// For more details visit: [`chain_id`][`crate::chain_id`] fn chain_id(&mut self) -> U256; /// Returns the **reducible** native balance of the supplied address. From ef52f4ca75f26946149e40284a6a3a62a31d8d02 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 11 Nov 2025 17:04:56 -0300 Subject: [PATCH 22/36] fix `block_hash` doc error --- crates/ink/src/env_access.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index bed1943d928..a803c66b3ba 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -462,7 +462,7 @@ where /// } /// /// #[ink(message)] - /// pub fn get_block_hash(&self, block_number: BlockNumber) -> H256 { + /// pub fn get_block_hash(&self, block_number: BlockNumber) -> ink_primitives::H256 { /// self.env().block_hash(block_number) /// } /// } @@ -472,7 +472,7 @@ where /// # Note /// /// For more details visit: [`ink_env::block_hash`] - pub fn block_hash(self, block_number: BlockNumber) -> ink_primitives::H256 { + pub fn block_hash(self, block_number: BlockNumber) -> H256 { ink_env::block_hash(block_number) } From c908a739a9740d48c25978c44c36e3de014582e0 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 11 Nov 2025 17:40:27 -0300 Subject: [PATCH 23/36] run formatting --- crates/ink/src/env_access.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index a803c66b3ba..9a01847f561 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -462,7 +462,10 @@ where /// } /// /// #[ink(message)] - /// pub fn get_block_hash(&self, block_number: BlockNumber) -> ink_primitives::H256 { + /// pub fn get_block_hash( + /// &self, + /// block_number: BlockNumber, + /// ) -> ink_primitives::H256 { /// self.env().block_hash(block_number) /// } /// } From 06ab2c57f3528f4a116b1ee79492d736e62f3d5a Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 25 Nov 2025 10:12:05 -0300 Subject: [PATCH 24/36] fix merge errors on changelog --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cda643de74f..70f38c8e610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased] ## Added +- Implements the API for the `pallet-revive` host functions `chain_id`, `balance_of`, `base_fee`, `origin`, `code_size`, `block_hash`, `block_author` - [#2719](https://github.com/use-ink/ink/pull/2719) - Implement `From` for "ink-as-dependency" contract refs - [#2728](https://github.com/use-ink/ink/pull/2728) ## Version 6.0.0-beta.1 @@ -19,9 +20,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `ink_precompiles` crate with ERC-20 assets precompile interface ‒ [#2686](https://github.com/use-ink/ink/pull/2686) ### Changed -- Implements the API for the `pallet-revive` host functions `chain_id`, `balance_of`, `base_fee`, `origin`, `code_size`, `block_hash`, `block_author` - [#2719](https://github.com/use-ink/ink/pull/2719) -- Refactor contract ref generation and add automatic re-exporting - [#2710](https://github.com/use-ink/ink/pull/2710) -- Implement and stabilize `terminate_contract` ‒ [2708](https://github.com/use-ink/ink/pull/2708) - Refactor contract ref generation and add automatic re-exporting ‒ [#2710](https://github.com/use-ink/ink/pull/2710) ## Version 6.0.0-beta From f6e334587fb0c63d140df2bc01d8b61fa48b8223 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 25 Nov 2025 15:37:59 -0300 Subject: [PATCH 25/36] start refactoring block_hash --- crates/env/src/api.rs | 8 +++++--- crates/env/src/backend.rs | 7 ++----- crates/env/src/engine/off_chain/impls.rs | 3 +-- crates/env/src/engine/on_chain/pallet_revive.rs | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index d2634ab153f..f1ce8dc2523 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -26,7 +26,6 @@ use ink_primitives::{ Sol, }, sol::SolResultEncode, - types::BlockNumber, }; use ink_storage_traits::Storable; use pallet_revive_uapi::ReturnFlags; @@ -156,9 +155,12 @@ pub fn code_size(addr: Address) -> u64 { /// Returns the block hash of the given block number. /// This is akin to the EVM [BLOCKHASH](https://www.evm.codes/?fork=cancun#40) opcode. -pub fn block_hash(block_number: BlockNumber) -> H256 { +pub fn block_hash(block_number: E::BlockNumber) -> H256 +where + E: Environment, +{ ::on_instance(|instance| { - TypedEnvBackend::block_hash(instance, block_number) + TypedEnvBackend::block_hash::(instance, block_number) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 9a80bb14122..6c0048d3ed2 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -20,10 +20,7 @@ use ink_primitives::{ H256, U256, sol::SolResultEncode, - types::{ - BlockNumber, - Environment, - }, + types::Environment, }; use ink_storage_traits::Storable; pub use pallet_revive_uapi::ReturnFlags; @@ -362,7 +359,7 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`block_hash`][`crate::block_hash`] - fn block_hash(&mut self, block_number: BlockNumber) -> H256; + fn block_hash(&mut self, block_number: E::BlockNumber) -> H256; /// Returns the current block author. /// This is akin to the EVM [COINBASE](https://www.evm.codes/?fork=cancun#41) opcode. diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index d6419a02b8a..c7b99bb6533 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -28,7 +28,6 @@ use ink_primitives::{ sol::SolResultEncode, types::{ AccountIdMapper, - BlockNumber, Environment, }, }; @@ -599,7 +598,7 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } - fn block_hash(&mut self, _block_number: BlockNumber) -> H256 { + fn block_hash(&mut self, _block_number: E::BlockNumber) -> H256 { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 0fbee95d656..bc9e16638ff 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -1068,7 +1068,7 @@ impl TypedEnvBackend for EnvInstance { ext::code_size(addr) } - fn block_hash(&mut self, block_number: BlockNumber) -> H256 { + fn block_hash(&mut self, block_number: E::BlockNumber) -> H256 { let mut scope = self.scoped_buffer(); let output: &mut [u8; 32] = scope.take(32).try_into().unwrap(); @@ -1078,7 +1078,7 @@ impl TypedEnvBackend for EnvInstance { bytes }; - ext::block_hash(&block_number, output); + ext::block_hash::(&block_number, output); H256::from_slice(output) } From e15d33d1aff4d878fc502d120a17930d1cf52459 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 25 Nov 2025 16:39:10 -0300 Subject: [PATCH 26/36] finish block_hash refatoring --- crates/env/src/engine/on_chain/pallet_revive.rs | 8 +++++--- crates/ink/src/env_access.rs | 7 +++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index bc9e16638ff..9f6edacd273 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -25,7 +25,6 @@ use ink_primitives::{ Sol, }, sol::SolResultEncode, - types::BlockNumber, }; use ink_storage_traits::{ Storable, @@ -83,6 +82,7 @@ use crate::{ types::FromLittleEndian, }; + /// The code hash of an existing account without code. /// This is the `keccak256` hash of empty data. /// ```no_compile @@ -1074,11 +1074,13 @@ impl TypedEnvBackend for EnvInstance { let block_number = { let mut bytes = [0u8; 32]; - bytes[..4].copy_from_slice(&block_number.to_le_bytes()); + let encoded = ::encode(&block_number); + // NOTE: panics if encoding is bigger than 32 bytes. + bytes[..encoded.len()].copy_from_slice(&encoded); bytes }; - ext::block_hash::(&block_number, output); + ext::block_hash(&block_number, output); H256::from_slice(output) } diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 997dfb5a2e3..4499faee59a 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -45,8 +45,7 @@ use ink_primitives::{ abi::{ Ink, Sol, - }, - types::BlockNumber, + } }; use pallet_revive_uapi::ReturnErrorCode; @@ -475,8 +474,8 @@ where /// # Note /// /// For more details visit: [`ink_env::block_hash`] - pub fn block_hash(self, block_number: BlockNumber) -> H256 { - ink_env::block_hash(block_number) + pub fn block_hash(self, block_number: E::BlockNumber) -> H256 { + ink_env::block_hash::(block_number) } /// Returns the current block author. From 00c0d7f38c591562af5035b8967b2acd5eaac76d Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 25 Nov 2025 18:21:35 -0300 Subject: [PATCH 27/36] use take_encoded to avoid allocation --- crates/env/src/engine/on_chain/pallet_revive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 9f6edacd273..aad092af661 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -1074,7 +1074,7 @@ impl TypedEnvBackend for EnvInstance { let block_number = { let mut bytes = [0u8; 32]; - let encoded = ::encode(&block_number); + let encoded = scope.take_encoded(&block_number); // NOTE: panics if encoding is bigger than 32 bytes. bytes[..encoded.len()].copy_from_slice(&encoded); bytes From 6971272440e25d54c5a788d4665e3e9adc7111d3 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Tue, 25 Nov 2025 18:35:40 -0300 Subject: [PATCH 28/36] run formatting --- crates/env/src/engine/on_chain/pallet_revive.rs | 1 - crates/ink/src/env_access.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index aad092af661..7706047a35d 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -82,7 +82,6 @@ use crate::{ types::FromLittleEndian, }; - /// The code hash of an existing account without code. /// This is the `keccak256` hash of empty data. /// ```no_compile diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 4499faee59a..933cc80b54b 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -45,7 +45,7 @@ use ink_primitives::{ abi::{ Ink, Sol, - } + }, }; use pallet_revive_uapi::ReturnErrorCode; From e79f5557878ad3dc76b5c2afeb9c901950878c47 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 12:12:51 -0300 Subject: [PATCH 29/36] define trait and start impl --- crates/env/src/api.rs | 18 +++++++++++++ crates/env/src/backend.rs | 25 +++++++++++++++++++ .../env/src/engine/on_chain/pallet_revive.rs | 4 +++ 3 files changed, 47 insertions(+) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index f1ce8dc2523..e93f86f1af4 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -170,6 +170,24 @@ pub fn block_author() -> Address { ::on_instance(TypedEnvBackend::block_author) } +/// Returns the returned data of the last contract call or contract instantiation, +/// starting from the given input data `offset`. +/// This is akin to the EVM [RETURNDATACOPY](https://www.evm.codes/?fork=cancun#3E) opcode. +pub fn return_data_copy(offset: u32) -> Vec { + ::on_instance(|instance| { + TypedEnvBackend::return_data_copy(instance, output, offset) + }) +} + +/// Returns the input data passed by the caller, +/// starting from the given input data `offset`. +/// This is akin to the EVM [CALLDATACOPY](https://www.evm.codes/?fork=cancun#37) opcode. +pub fn call_data_copy(offset: u32) -> Vec { + ::on_instance(|instance| { + TypedEnvBackend::call_data_copy(instance, output, offset) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 6c0048d3ed2..c741d0a90ac 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -369,6 +369,31 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`block_author`][`crate::block_author`] fn block_author(&mut self) -> Address; + /// Returns the returned data of the last contract call or contract instantiation, + /// starting from the given input data `offset`. + /// This is akin to the EVM [RETURNDATACOPY](https://www.evm.codes/?fork=cancun#3E) opcode. + /// + /// # Note + /// + /// For more details visit: [`return_data_copy`][`crate::return_data_copy`] + fn return_data_copy(&mut self, offset: u32) -> Vec; + + /// Returns the input data passed by the caller, + /// starting from the given input data `offset`. + /// This is akin to the EVM [CALLDATACOPY](https://www.evm.codes/?fork=cancun#37) opcode. + /// + /// # Error + /// + /// This function traps if: + /// - the input was previously forwarded by a + /// [`call()`][`pallet_revive_uapi::HostFn::call()`]. + /// - the `output` buffer is located in an PolkaVM invalid memory range. + /// + /// # Note + /// + /// For more details visit: [`call_data_copy`][`crate::call_data_copy`] + fn call_data_copy(&mut self, offset: u32) -> Vec; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 7706047a35d..2418fad18b1 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -1093,6 +1093,10 @@ impl TypedEnvBackend for EnvInstance { h160.into() } + fn return_data_copy(&mut self, offset: u32) -> Vec { + + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); From 1822f5c76dbd31d5406322bf42ed1caba19f7ffd Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 12:27:48 -0300 Subject: [PATCH 30/36] implement `call_data_load` hostfn --- crates/env/src/api.rs | 25 ++++++-------- crates/env/src/backend.rs | 29 +++++----------- crates/env/src/engine/off_chain/impls.rs | 4 +++ .../env/src/engine/on_chain/pallet_revive.rs | 6 +++- crates/ink/src/env_access.rs | 34 +++++++++++++++++++ .../ink/tests/ui/contract/pass/env-access.rs | 2 ++ .../internal/misc-evm-getters-hostfns/lib.rs | 34 +++++++++++++++++++ 7 files changed, 99 insertions(+), 35 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index e93f86f1af4..100bc9ec4ff 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -170,21 +170,18 @@ pub fn block_author() -> Address { ::on_instance(TypedEnvBackend::block_author) } -/// Returns the returned data of the last contract call or contract instantiation, -/// starting from the given input data `offset`. -/// This is akin to the EVM [RETURNDATACOPY](https://www.evm.codes/?fork=cancun#3E) opcode. -pub fn return_data_copy(offset: u32) -> Vec { - ::on_instance(|instance| { - TypedEnvBackend::return_data_copy(instance, output, offset) - }) -} - -/// Returns the input data passed by the caller, -/// starting from the given input data `offset`. -/// This is akin to the EVM [CALLDATACOPY](https://www.evm.codes/?fork=cancun#37) opcode. -pub fn call_data_copy(offset: u32) -> Vec { +/// Returns the U256 value at the given offset from the input passed by the caller, +/// akin to the EVM [CALLDATALOAD](https://www.evm.codes/?fork=cancun#35) opcode. +/// +/// # Note +/// +/// - If `offset` is out of bounds, a value of zero will be returned. +/// - If `offset` is in bounds but there is not enough call data, the available data is +/// right-padded in order to fill a whole U256 value. +/// - The data returned is a little endian U256 integer value. +pub fn call_data_load(offset: u32) -> U256 { ::on_instance(|instance| { - TypedEnvBackend::call_data_copy(instance, output, offset) + TypedEnvBackend::call_data_load(instance, offset) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index c741d0a90ac..0d9a0388d6e 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -369,30 +369,19 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`block_author`][`crate::block_author`] fn block_author(&mut self) -> Address; - /// Returns the returned data of the last contract call or contract instantiation, - /// starting from the given input data `offset`. - /// This is akin to the EVM [RETURNDATACOPY](https://www.evm.codes/?fork=cancun#3E) opcode. + /// Retrurns the U256 value at given `offset` from the input passed by the caller + /// into the supplied buffer. + /// This is akin to the EVM [CALLDATALOAD](https://www.evm.codes/?fork=cancun#35) opcode. /// /// # Note /// - /// For more details visit: [`return_data_copy`][`crate::return_data_copy`] - fn return_data_copy(&mut self, offset: u32) -> Vec; - - /// Returns the input data passed by the caller, - /// starting from the given input data `offset`. - /// This is akin to the EVM [CALLDATACOPY](https://www.evm.codes/?fork=cancun#37) opcode. - /// - /// # Error - /// - /// This function traps if: - /// - the input was previously forwarded by a - /// [`call()`][`pallet_revive_uapi::HostFn::call()`]. - /// - the `output` buffer is located in an PolkaVM invalid memory range. - /// - /// # Note + /// - If `offset` is out of bounds, a value of zero will be returned. + /// - If `offset` is in bounds but there is not enough call data, the available data + /// is right-padded in order to fill a whole U256 value. + /// - The data returned is a little endian U256 integer value. /// - /// For more details visit: [`call_data_copy`][`crate::call_data_copy`] - fn call_data_copy(&mut self, offset: u32) -> Vec; + /// For more details visit: [`call_data_load`][`crate::call_data_load`] + fn call_data_load(&mut self, offset: u32) -> U256; /// Returns the transferred value for the contract execution. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index c7b99bb6533..7a706a2aacf 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -618,6 +618,10 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn call_data_load(&mut self, _offset: u32) -> U256 { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn transferred_value(&mut self) -> U256 { self.get_property(Engine::value_transferred) .unwrap_or_else(|error| { diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 2418fad18b1..543c7b0a00b 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -1093,8 +1093,12 @@ impl TypedEnvBackend for EnvInstance { h160.into() } - fn return_data_copy(&mut self, offset: u32) -> Vec { + fn call_data_load(&mut self, offset: u32) -> U256 { + let mut scope = self.scoped_buffer(); + let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + ext::call_data_load(u256, offset); + U256::from_le_bytes(*u256) } fn transferred_value(&mut self) -> U256 { diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 933cc80b54b..8b8bf8c2953 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -274,6 +274,40 @@ where ink_env::return_data_size() } + /// Returns the U256 value at the given offset from the input passed by the caller, + /// akin to the EVM [CALLDATALOAD](https://www.evm.codes/?fork=cancun#35) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn get_call_data_at_offset(&self, offset: u32) -> U256 { + /// self.env().call_data_load(offset) + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::call_data_load`] + pub fn call_data_load(self, offset: u32) -> U256 { + ink_env::call_data_load(offset) + } + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. /// diff --git a/crates/ink/tests/ui/contract/pass/env-access.rs b/crates/ink/tests/ui/contract/pass/env-access.rs index 672443cff6e..9e534bba08d 100644 --- a/crates/ink/tests/ui/contract/pass/env-access.rs +++ b/crates/ink/tests/ui/contract/pass/env-access.rs @@ -25,6 +25,7 @@ mod contract { let _ = Self::env().block_author(); let _ = Self::env().transferred_value(); let _ = Self::env().weight_to_fee(0); + let _ = Self::env().call_data_load(0); Self {} } @@ -47,6 +48,7 @@ mod contract { let _ = self.env().block_author(); let _ = self.env().transferred_value(); let _ = self.env().weight_to_fee(0); + let _ = self.env().call_data_load(0); } } } diff --git a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs index 24109cbb7bb..408a71253c0 100644 --- a/integration-tests/internal/misc-evm-getters-hostfns/lib.rs +++ b/integration-tests/internal/misc-evm-getters-hostfns/lib.rs @@ -60,6 +60,12 @@ mod misc_evm_getters_hostfns { pub fn block_author(&self) -> Address { self.env().block_author() } + + /// Checks that the host function `call_data_load` works + #[ink(message)] + pub fn call_data_load(&self, offset: u32) -> U256 { + self.env().call_data_load(offset) + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -283,5 +289,33 @@ mod misc_evm_getters_hostfns { Ok(()) } + + #[ink_e2e::test] + async fn e2e_call_data_load_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let contract = client + .instantiate( + "misc_evm_getters_hostfns", + &ink_e2e::alice(), + &mut MiscEVMGettersfnsRef::new(), + ) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // then + let _call_res = client + .call(&ink_e2e::alice(), &call_builder.call_data_load(0)) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + Ok(()) + } } } From e0fe6cc7cb24e07ead0a33de5516a6538376c695 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 12:34:54 -0300 Subject: [PATCH 31/36] fix call_data_load docs --- crates/env/src/backend.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 0d9a0388d6e..da597166d95 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -369,8 +369,7 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`block_author`][`crate::block_author`] fn block_author(&mut self) -> Address; - /// Retrurns the U256 value at given `offset` from the input passed by the caller - /// into the supplied buffer. + /// Returns the U256 value at given `offset` from the input passed by the caller. /// This is akin to the EVM [CALLDATALOAD](https://www.evm.codes/?fork=cancun#35) opcode. /// /// # Note From 612af80628e93a6a430686eb4b5bcc002d5c90ca Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 17:32:26 -0300 Subject: [PATCH 32/36] implement set_storage and set_transient_storage --- crates/env/src/api.rs | 24 +++ crates/env/src/backend.rs | 20 ++ crates/env/src/engine/off_chain/impls.rs | 10 + .../env/src/engine/on_chain/pallet_revive.rs | 15 ++ crates/ink/src/env_access.rs | 76 ++++++++ .../internal/storage-hostfns/Cargo.toml | 28 +++ .../internal/storage-hostfns/lib.rs | 171 ++++++++++++++++++ 7 files changed, 344 insertions(+) create mode 100755 integration-tests/internal/storage-hostfns/Cargo.toml create mode 100644 integration-tests/internal/storage-hostfns/lib.rs diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 100bc9ec4ff..c44ead471d6 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -185,6 +185,30 @@ pub fn call_data_load(offset: u32) -> U256 { }) } +/// Sets the storage entry for a fixed 256-bit key with a fixed 256-bit value, +/// akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. +/// +/// If the provided value is all zeros then the key is cleared (i.e. deleted). +/// Returns the size (in bytes) of the pre-existing value at the specified key, if any. +pub fn set_storage(key: U256, value: &[u8; 32]) -> Option +{ + ::on_instance(|instance| { + TypedEnvBackend::set_storage(instance, key, value) + }) +} + +/// Sets the transient storage entry for a fixed 256-bit key with a fixed 256-bit value, +/// akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. +/// +/// If the provided value is all zeros then the key is cleared (i.e. deleted). +/// Returns the size (in bytes) of the pre-existing value at the specified key, if any. +pub fn set_transient_storage(key: U256, value: &[u8; 32]) -> Option +{ + ::on_instance(|instance| { + TypedEnvBackend::set_transient_storage(instance, key, value) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index da597166d95..2f5a7ff45f1 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -382,6 +382,26 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`call_data_load`][`crate::call_data_load`] fn call_data_load(&mut self, offset: u32) -> U256; + /// Sets the storage entry for a fixed 256‑bit key with a fixed 256‑bit value. + /// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted). + /// Returns the size (in bytes) of the pre‑existing value at the specified key, if any. + /// This is akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. + /// + /// # Note + /// + /// For more details visit: [`set_storage`][`crate::set_storage`] + fn set_storage(&mut self, key: U256, value: &[u8; 32]) -> Option; + + /// Sets the transient storage entry for a fixed 256‑bit key with a fixed 256‑bit value. + /// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted). + /// Returns the size (in bytes) of the pre‑existing value at the specified key, if any. + /// This is akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. + /// + /// # Note + /// + /// For more details visit: [`set_transient_storage`][`crate::set_transient_storage`] + fn set_transient_storage(&mut self, key: U256, value: &[u8; 32]) -> Option; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 7a706a2aacf..5cd30443ff4 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -622,6 +622,16 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn set_storage(&mut self, _key: U256, _value: &[u8; 32]) -> Option + { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + + fn set_transient_storage(&mut self, _key: U256, _value: &[u8; 32]) -> Option + { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn transferred_value(&mut self) -> U256 { self.get_property(Engine::value_transferred) .unwrap_or_else(|error| { diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 543c7b0a00b..cf18549ef66 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -647,6 +647,7 @@ fn decode_bytes(input: &[u8], out: &mut [u8]) -> usize { } const STORAGE_FLAGS: StorageFlags = StorageFlags::empty(); +const TRANSIENT_STORAGE_FLAGS: StorageFlags = StorageFlags::TRANSIENT; impl EnvBackend for EnvInstance { fn set_contract_storage(&mut self, key: &K, value: &V) -> Option @@ -1101,6 +1102,20 @@ impl TypedEnvBackend for EnvInstance { U256::from_le_bytes(*u256) } + fn set_storage(&mut self, key: U256, value: &[u8; 32]) -> Option + { + let mut scope = self.scoped_buffer(); + let key: &mut [u8; 32] = scope.take_encoded(&key).try_into().unwrap(); + ext::set_storage_or_clear(STORAGE_FLAGS, key, value) + } + + fn set_transient_storage(&mut self, key: U256, value: &[u8; 32]) -> Option + { + let mut scope = self.scoped_buffer(); + let key: &mut [u8; 32] = scope.take_encoded(&key).try_into().unwrap(); + ext::set_storage_or_clear(TRANSIENT_STORAGE_FLAGS, key, value) + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 8b8bf8c2953..aa3bd6e276d 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -308,6 +308,82 @@ where ink_env::call_data_load(offset) } + /// Sets the storage entry for a fixed 256-bit key with a fixed 256-bit value. + /// If the provided 32-byte value is all zeros then the key is cleared (i.e. deleted). + /// Returns the size (in bytes) of the pre-existing value at the specified key, if any. + /// This is akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn store_value(&self) { + /// let key = U256::from(42u32); + /// let value = [0xDEu8; 32]; + /// self.env().set_storage(key, &value); + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::set_storage`] + pub fn set_storage(self, key: U256, value: &[u8; 32]) -> Option { + ink_env::set_storage(key, value) + } + + /// Sets the transient storage entry for a fixed 256-bit key with a fixed 256-bit value. + /// If the provided 32-byte value is all zeros then the key is cleared (i.e. deleted). + /// Returns the size (in bytes) of the pre-existing value at the specified key, if any. + /// This is akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn store_transient(&self) { + /// let key = U256::from(42u32); + /// let value = [0xABu8; 32]; + /// self.env().set_transient_storage(key, &value); + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::set_transient_storage`] + pub fn set_transient_storage(self, key: U256, value: &[u8; 32]) -> Option { + ink_env::set_transient_storage(key, value) + } + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. /// diff --git a/integration-tests/internal/storage-hostfns/Cargo.toml b/integration-tests/internal/storage-hostfns/Cargo.toml new file mode 100755 index 00000000000..12560413065 --- /dev/null +++ b/integration-tests/internal/storage-hostfns/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "storage-hostfns" +description = "E2E tests for storage related host functions" +version = "6.0.0-beta" +authors = ["Use Ink "] +edition = "2021" +publish = false + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false } + +[dev-dependencies] +ink_e2e = { path = "../../../crates/e2e" } +ink_primitives = { path = "../../../crates/primitives" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", +] +ink-as-dependency = [] +e2e-tests = [] + +[package.metadata.ink-lang] +abi = "ink" diff --git a/integration-tests/internal/storage-hostfns/lib.rs b/integration-tests/internal/storage-hostfns/lib.rs new file mode 100644 index 00000000000..f30809688f6 --- /dev/null +++ b/integration-tests/internal/storage-hostfns/lib.rs @@ -0,0 +1,171 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] +#![allow(clippy::new_without_default)] + +#[ink::contract] +mod storage_hostfns { + use ink::U256; + + #[ink(storage)] + pub struct Storagefns {} + + impl Storagefns { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + + /// Sets a value in persistent storage. + #[ink(message)] + pub fn set_storage(&self, key: U256, value: [u8; 32]) -> Option { + self.env().set_storage(key, &value) + } + + /// Sets a value in transient storage. + #[ink(message)] + pub fn set_transient_storage(&self, key: U256, value: [u8; 32]) -> Option { + self.env().set_transient_storage(key, &value) + } + + /// Clears a persistent storage entry by setting value to all zeros. + #[ink(message)] + pub fn clear_storage(&self, key: U256) -> Option { + self.env().set_storage(key, &[0u8; 32]) + } + + /// Clears a transient storage entry by setting value to all zeros. + #[ink(message)] + pub fn set_clear_transient_storage(&self, key: U256, value: [u8; 32]) -> Option { + self.env().set_transient_storage(key, &value); + self.env().set_transient_storage(key, &[0u8; 32]) + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn set_storage_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // when - set a value + let key = U256::from(42u32); + let value = [0xDEu8; 32]; + let result = client + .call(&ink_e2e::alice(), &call_builder.set_storage(key, value)) + .submit() + .await?; + + // then - first set returns None (no previous value) + assert_eq!(result.return_value(), None); + + Ok(()) + } + + #[ink_e2e::test] + async fn clear_storage_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let key = U256::from(42u32); + let value = [0xDEu8; 32]; + + // when - set a value + let _ = client + .call(&ink_e2e::alice(), &call_builder.set_storage(key, value)) + .submit() + .await?; + + // when - clear it + let result = client + .call(&ink_e2e::alice(), &call_builder.clear_storage(key)) + .submit() + .await?; + + // then - should return size of previous value (32 bytes) + assert_eq!(result.return_value(), Some(32u32)); + + Ok(()) + } + + #[ink_e2e::test] + async fn set_transient_storage_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // when - set a transient value + let key = U256::from(50u32); + let value = [0xABu8; 32]; + let result = client + .call(&ink_e2e::alice(), &call_builder.set_transient_storage(key, value)) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + // then - first set returns None + assert_eq!(result.return_value(), None); + + Ok(()) + } + + #[ink_e2e::test] + async fn clear_transient_storage_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let key = U256::from(50u32); + let value = [0x1u8; 32]; + + let result = client + .call(&ink_e2e::alice(), &call_builder.set_clear_transient_storage(key, value)) + .submit() + .await + .unwrap_or_else(|err| { + panic!("call failed: {:#?}", err); + }); + + // then - should return size of previous value (32 bytes) + assert_eq!(result.return_value(), Some(32u32)); + + Ok(()) + } + } +} From 1139945f3bf59ecd4101353ab3f22394452c8056 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 17:44:18 -0300 Subject: [PATCH 33/36] implement get_storage and get_transient_storage --- crates/env/src/api.rs | 18 +++ crates/env/src/backend.rs | 18 +++ crates/env/src/engine/off_chain/impls.rs | 8 ++ .../env/src/engine/on_chain/pallet_revive.rs | 16 +++ crates/ink/src/env_access.rs | 72 ++++++++++++ .../internal/storage-hostfns/lib.rs | 108 ++++++++++++++++++ 6 files changed, 240 insertions(+) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index c44ead471d6..dabcc730035 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -209,6 +209,24 @@ pub fn set_transient_storage(key: U256, value: &[u8; 32]) -> Option }) } +/// Retrieves the storage entry for a fixed 256-bit key. +/// If the key does not exist, it returns 32 zero bytes. +/// This is akin to the EVM [SLOAD](https://www.evm.codes/?fork=cancun#54) opcode. +pub fn get_storage(key: U256) -> [u8; 32] { + ::on_instance(|instance| { + TypedEnvBackend::get_storage(instance, key) + }) +} + +/// Retrieves the transient storage entry for a fixed 256-bit key. +/// If the key does not exist, it returns 32 zero bytes. +/// This is akin to the EVM [TLOAD](https://www.evm.codes/?fork=cancun#5C) opcode. +pub fn get_transient_storage(key: U256) -> [u8; 32] { + ::on_instance(|instance| { + TypedEnvBackend::get_transient_storage(instance, key) + }) +} + /// Returns the transferred value for the contract execution. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 2f5a7ff45f1..76d0ecae178 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -402,6 +402,24 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`set_transient_storage`][`crate::set_transient_storage`] fn set_transient_storage(&mut self, key: U256, value: &[u8; 32]) -> Option; + /// Retrieves the storage entry for a fixed 256‑bit key. + /// If the key does not exist, it returns 32 zero bytes. + /// This is akin to the EVM [SLOAD](https://www.evm.codes/?fork=cancun#54) opcode. + /// + /// # Note + /// + /// For more details visit: [`get_storage`][`crate::get_storage`] + fn get_storage(&mut self, key: U256) -> [u8; 32]; + + /// Retrieves the transient storage entry for a fixed 256‑bit key. + /// If the key does not exist, it returns 32 zero bytes. + /// This is akin to the EVM [TLOAD](https://www.evm.codes/?fork=cancun#5C) opcode. + /// + /// # Note + /// + /// For more details visit: [`get_transient_storage`][`crate::get_transient_storage`] + fn get_transient_storage(&mut self, key: U256) -> [u8; 32]; + /// Returns the transferred value for the contract execution. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 5cd30443ff4..0a1162b6197 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -632,6 +632,14 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } + fn get_storage(&mut self, _key: U256) -> [u8; 32] { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + + fn get_transient_storage(&mut self, _key: U256) -> [u8; 32] { + unimplemented!("not implemented, the off-chain environment will be removed"); + } + fn transferred_value(&mut self) -> U256 { self.get_property(Engine::value_transferred) .unwrap_or_else(|error| { diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index cf18549ef66..4281dc10b7b 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -1116,6 +1116,22 @@ impl TypedEnvBackend for EnvInstance { ext::set_storage_or_clear(TRANSIENT_STORAGE_FLAGS, key, value) } + fn get_storage(&mut self, key: U256) -> [u8; 32] { + let mut scope = self.scoped_buffer(); + let key: &mut [u8; 32] = scope.take_encoded(&key).try_into().unwrap(); + let value: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + ext::get_storage_or_zero(STORAGE_FLAGS, key, value); + *value + } + + fn get_transient_storage(&mut self, key: U256) -> [u8; 32] { + let mut scope = self.scoped_buffer(); + let key: &mut [u8; 32] = scope.take_encoded(&key).try_into().unwrap(); + let value: &mut [u8; 32] = scope.take(32).try_into().unwrap(); + ext::get_storage_or_zero(TRANSIENT_STORAGE_FLAGS, key, value); + *value + } + fn transferred_value(&mut self) -> U256 { let mut scope = self.scoped_buffer(); let u256: &mut [u8; 32] = scope.take(32).try_into().unwrap(); diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index aa3bd6e276d..ac0b45db128 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -384,6 +384,78 @@ where ink_env::set_transient_storage(key, value) } + /// Retrieves the storage entry for a fixed 256-bit key. + /// If the key does not exist, it returns 32 zero bytes. + /// This is akin to the EVM [SLOAD](https://www.evm.codes/?fork=cancun#54) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn load_value(&self) -> [u8; 32] { + /// let key = U256::from(42u32); + /// self.env().get_storage(key) + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::get_storage`] + pub fn get_storage(self, key: U256) -> [u8; 32] { + ink_env::get_storage(key) + } + + /// Retrieves the transient storage entry for a fixed 256-bit key. + /// If the key does not exist, it returns 32 zero bytes. + /// This is akin to the EVM [TLOAD](https://www.evm.codes/?fork=cancun#5C) opcode. + /// + /// # Example + /// + /// ``` + /// #[ink::contract] + /// mod my_contract { + /// use ink::U256; + /// + /// #[ink(storage)] + /// pub struct MyContract; + /// + /// impl MyContract { + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self {} + /// } + /// + /// #[ink(message)] + /// pub fn load_transient(&self) -> [u8; 32] { + /// let key = U256::from(42u32); + /// self.env().get_transient_storage(key) + /// } + /// } + /// } + /// ``` + /// + /// # Note + /// + /// For more details visit: [`ink_env::get_transient_storage`] + pub fn get_transient_storage(self, key: U256) -> [u8; 32] { + ink_env::get_transient_storage(key) + } + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID, /// akin to the EVM [CHAINID](https://www.evm.codes/?fork=cancun#46) opcode. /// diff --git a/integration-tests/internal/storage-hostfns/lib.rs b/integration-tests/internal/storage-hostfns/lib.rs index f30809688f6..8e0da566c74 100644 --- a/integration-tests/internal/storage-hostfns/lib.rs +++ b/integration-tests/internal/storage-hostfns/lib.rs @@ -38,6 +38,25 @@ mod storage_hostfns { self.env().set_transient_storage(key, &value); self.env().set_transient_storage(key, &[0u8; 32]) } + + /// Retrieves a value from persistent storage. + #[ink(message)] + pub fn get_storage(&self, key: U256) -> [u8; 32] { + self.env().get_storage(key) + } + + /// Retrieves a value from transient storage. + #[ink(message)] + pub fn get_transient_storage(&self, key: U256) -> [u8; 32] { + self.env().get_transient_storage(key) + } + + /// Sets a transient value and immediately retrieves it in the same transaction. + #[ink(message)] + pub fn set_and_get_transient_storage(&self, key: U256, value: [u8; 32]) -> [u8; 32] { + self.env().set_transient_storage(key, &value); + self.env().get_transient_storage(key) + } } #[cfg(all(test, feature = "e2e-tests"))] @@ -167,5 +186,94 @@ mod storage_hostfns { Ok(()) } + + #[ink_e2e::test] + async fn get_storage_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let key = U256::from(100u32); + let value = [0xAAu8; 32]; + + // when - set a value + let _ = client + .call(&ink_e2e::alice(), &call_builder.set_storage(key, value)) + .submit() + .await?; + + // when - get the value + let result = client + .call(&ink_e2e::alice(), &call_builder.get_storage(key)) + .submit() + .await?; + + // then - should retrieve the same value + assert_eq!(result.return_value(), value); + + Ok(()) + } + + #[ink_e2e::test] + async fn set_and_get_transient_storage_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let key = U256::from(101u32); + let value = [0xBBu8; 32]; + + // when - set and get transient value in same call + let result = client + .call(&ink_e2e::alice(), &call_builder.set_and_get_transient_storage(key, value)) + .submit() + .await?; + + // then - should retrieve the same value + assert_eq!(result.return_value(), value); + + Ok(()) + } + + #[ink_e2e::test] + async fn get_nonexistent_storage_returns_zeros( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = StoragefnsRef::new(); + let contract = client + .instantiate("storage-hostfns", &ink_e2e::alice(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + let nonexistent_key = U256::from(999u32); + + // when - get a nonexistent value + let result = client + .call(&ink_e2e::alice(), &call_builder.get_storage(nonexistent_key)) + .submit() + .await?; + + // then - should return 32 zero bytes + assert_eq!(result.return_value(), [0u8; 32]); + + Ok(()) + } } } From 87eec110c2a60aa2e6a8ab8988a832a1c0e9df66 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 17:45:11 -0300 Subject: [PATCH 34/36] run formatting --- crates/env/src/api.rs | 6 ++-- crates/env/src/backend.rs | 18 +++++------ crates/env/src/engine/off_chain/impls.rs | 6 ++-- .../env/src/engine/on_chain/pallet_revive.rs | 6 ++-- crates/ink/src/env_access.rs | 12 +++---- .../internal/storage-hostfns/lib.rs | 32 +++++++++++++++---- 6 files changed, 47 insertions(+), 33 deletions(-) diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index dabcc730035..cb2bd4185b3 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -190,8 +190,7 @@ pub fn call_data_load(offset: u32) -> U256 { /// /// If the provided value is all zeros then the key is cleared (i.e. deleted). /// Returns the size (in bytes) of the pre-existing value at the specified key, if any. -pub fn set_storage(key: U256, value: &[u8; 32]) -> Option -{ +pub fn set_storage(key: U256, value: &[u8; 32]) -> Option { ::on_instance(|instance| { TypedEnvBackend::set_storage(instance, key, value) }) @@ -202,8 +201,7 @@ pub fn set_storage(key: U256, value: &[u8; 32]) -> Option /// /// If the provided value is all zeros then the key is cleared (i.e. deleted). /// Returns the size (in bytes) of the pre-existing value at the specified key, if any. -pub fn set_transient_storage(key: U256, value: &[u8; 32]) -> Option -{ +pub fn set_transient_storage(key: U256, value: &[u8; 32]) -> Option { ::on_instance(|instance| { TypedEnvBackend::set_transient_storage(instance, key, value) }) diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 76d0ecae178..ac76fa7f031 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -383,19 +383,19 @@ pub trait TypedEnvBackend: EnvBackend { fn call_data_load(&mut self, offset: u32) -> U256; /// Sets the storage entry for a fixed 256‑bit key with a fixed 256‑bit value. - /// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted). - /// Returns the size (in bytes) of the pre‑existing value at the specified key, if any. - /// This is akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. + /// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted). + /// Returns the size (in bytes) of the pre‑existing value at the specified key, if + /// any. This is akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. /// /// # Note /// /// For more details visit: [`set_storage`][`crate::set_storage`] fn set_storage(&mut self, key: U256, value: &[u8; 32]) -> Option; - /// Sets the transient storage entry for a fixed 256‑bit key with a fixed 256‑bit value. - /// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted). - /// Returns the size (in bytes) of the pre‑existing value at the specified key, if any. - /// This is akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. + /// Sets the transient storage entry for a fixed 256‑bit key with a fixed 256‑bit + /// value. If the provided 32‑byte value is all zeros then the key is cleared + /// (i.e. deleted). Returns the size (in bytes) of the pre‑existing value at the + /// specified key, if any. This is akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. /// /// # Note /// @@ -403,7 +403,7 @@ pub trait TypedEnvBackend: EnvBackend { fn set_transient_storage(&mut self, key: U256, value: &[u8; 32]) -> Option; /// Retrieves the storage entry for a fixed 256‑bit key. - /// If the key does not exist, it returns 32 zero bytes. + /// If the key does not exist, it returns 32 zero bytes. /// This is akin to the EVM [SLOAD](https://www.evm.codes/?fork=cancun#54) opcode. /// /// # Note @@ -412,7 +412,7 @@ pub trait TypedEnvBackend: EnvBackend { fn get_storage(&mut self, key: U256) -> [u8; 32]; /// Retrieves the transient storage entry for a fixed 256‑bit key. - /// If the key does not exist, it returns 32 zero bytes. + /// If the key does not exist, it returns 32 zero bytes. /// This is akin to the EVM [TLOAD](https://www.evm.codes/?fork=cancun#5C) opcode. /// /// # Note diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 0a1162b6197..d5e7bc4a92e 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -622,13 +622,11 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("not implemented, the off-chain environment will be removed"); } - fn set_storage(&mut self, _key: U256, _value: &[u8; 32]) -> Option - { + fn set_storage(&mut self, _key: U256, _value: &[u8; 32]) -> Option { unimplemented!("not implemented, the off-chain environment will be removed"); } - fn set_transient_storage(&mut self, _key: U256, _value: &[u8; 32]) -> Option - { + fn set_transient_storage(&mut self, _key: U256, _value: &[u8; 32]) -> Option { unimplemented!("not implemented, the off-chain environment will be removed"); } diff --git a/crates/env/src/engine/on_chain/pallet_revive.rs b/crates/env/src/engine/on_chain/pallet_revive.rs index 4281dc10b7b..89272e07f05 100644 --- a/crates/env/src/engine/on_chain/pallet_revive.rs +++ b/crates/env/src/engine/on_chain/pallet_revive.rs @@ -1102,15 +1102,13 @@ impl TypedEnvBackend for EnvInstance { U256::from_le_bytes(*u256) } - fn set_storage(&mut self, key: U256, value: &[u8; 32]) -> Option - { + fn set_storage(&mut self, key: U256, value: &[u8; 32]) -> Option { let mut scope = self.scoped_buffer(); let key: &mut [u8; 32] = scope.take_encoded(&key).try_into().unwrap(); ext::set_storage_or_clear(STORAGE_FLAGS, key, value) } - fn set_transient_storage(&mut self, key: U256, value: &[u8; 32]) -> Option - { + fn set_transient_storage(&mut self, key: U256, value: &[u8; 32]) -> Option { let mut scope = self.scoped_buffer(); let key: &mut [u8; 32] = scope.take_encoded(&key).try_into().unwrap(); ext::set_storage_or_clear(TRANSIENT_STORAGE_FLAGS, key, value) diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index ac0b45db128..91d163f8586 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -310,8 +310,8 @@ where /// Sets the storage entry for a fixed 256-bit key with a fixed 256-bit value. /// If the provided 32-byte value is all zeros then the key is cleared (i.e. deleted). - /// Returns the size (in bytes) of the pre-existing value at the specified key, if any. - /// This is akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. + /// Returns the size (in bytes) of the pre-existing value at the specified key, if + /// any. This is akin to the EVM [SSTORE](https://www.evm.codes/?fork=cancun#55) opcode. /// /// # Example /// @@ -346,10 +346,10 @@ where ink_env::set_storage(key, value) } - /// Sets the transient storage entry for a fixed 256-bit key with a fixed 256-bit value. - /// If the provided 32-byte value is all zeros then the key is cleared (i.e. deleted). - /// Returns the size (in bytes) of the pre-existing value at the specified key, if any. - /// This is akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. + /// Sets the transient storage entry for a fixed 256-bit key with a fixed 256-bit + /// value. If the provided 32-byte value is all zeros then the key is cleared + /// (i.e. deleted). Returns the size (in bytes) of the pre-existing value at the + /// specified key, if any. This is akin to the EVM [TSTORE](https://www.evm.codes/?fork=cancun#5D) opcode. /// /// # Example /// diff --git a/integration-tests/internal/storage-hostfns/lib.rs b/integration-tests/internal/storage-hostfns/lib.rs index 8e0da566c74..3184988b467 100644 --- a/integration-tests/internal/storage-hostfns/lib.rs +++ b/integration-tests/internal/storage-hostfns/lib.rs @@ -34,7 +34,11 @@ mod storage_hostfns { /// Clears a transient storage entry by setting value to all zeros. #[ink(message)] - pub fn set_clear_transient_storage(&self, key: U256, value: [u8; 32]) -> Option { + pub fn set_clear_transient_storage( + &self, + key: U256, + value: [u8; 32], + ) -> Option { self.env().set_transient_storage(key, &value); self.env().set_transient_storage(key, &[0u8; 32]) } @@ -53,7 +57,11 @@ mod storage_hostfns { /// Sets a transient value and immediately retrieves it in the same transaction. #[ink(message)] - pub fn set_and_get_transient_storage(&self, key: U256, value: [u8; 32]) -> [u8; 32] { + pub fn set_and_get_transient_storage( + &self, + key: U256, + value: [u8; 32], + ) -> [u8; 32] { self.env().set_transient_storage(key, &value); self.env().get_transient_storage(key) } @@ -144,7 +152,10 @@ mod storage_hostfns { let key = U256::from(50u32); let value = [0xABu8; 32]; let result = client - .call(&ink_e2e::alice(), &call_builder.set_transient_storage(key, value)) + .call( + &ink_e2e::alice(), + &call_builder.set_transient_storage(key, value), + ) .submit() .await .unwrap_or_else(|err| { @@ -174,7 +185,10 @@ mod storage_hostfns { let value = [0x1u8; 32]; let result = client - .call(&ink_e2e::alice(), &call_builder.set_clear_transient_storage(key, value)) + .call( + &ink_e2e::alice(), + &call_builder.set_clear_transient_storage(key, value), + ) .submit() .await .unwrap_or_else(|err| { @@ -239,7 +253,10 @@ mod storage_hostfns { // when - set and get transient value in same call let result = client - .call(&ink_e2e::alice(), &call_builder.set_and_get_transient_storage(key, value)) + .call( + &ink_e2e::alice(), + &call_builder.set_and_get_transient_storage(key, value), + ) .submit() .await?; @@ -266,7 +283,10 @@ mod storage_hostfns { // when - get a nonexistent value let result = client - .call(&ink_e2e::alice(), &call_builder.get_storage(nonexistent_key)) + .call( + &ink_e2e::alice(), + &call_builder.get_storage(nonexistent_key), + ) .submit() .await?; From e9a079f02476a4f9f6aef8d8f196b670e9ccb158 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Sun, 30 Nov 2025 19:57:16 -0300 Subject: [PATCH 35/36] add entry to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70f38c8e610..29619665fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased] ## Added +- Implements the API for the `pallet-revive` host functions `set_storage_or_clear`, `get_storage_or_zero`, `call_data_load`, `return_data_copy` - [#2739](https://github.com/use-ink/ink/pull/2739) - Implements the API for the `pallet-revive` host functions `chain_id`, `balance_of`, `base_fee`, `origin`, `code_size`, `block_hash`, `block_author` - [#2719](https://github.com/use-ink/ink/pull/2719) - Implement `From` for "ink-as-dependency" contract refs - [#2728](https://github.com/use-ink/ink/pull/2728) From 8c94c0232751250dea5267ddefa722b17157ed37 Mon Sep 17 00:00:00 2001 From: Lucas Grasso Date: Thu, 18 Dec 2025 02:09:37 -0300 Subject: [PATCH 36/36] Update CHANGELOG with new API implementations --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72bf791a68f..24c6581bbb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased] -## Added +### Added - Implements the API for the `pallet-revive` host functions `set_storage_or_clear`, `get_storage_or_zero`, `call_data_load`, `return_data_copy` - [#2739](https://github.com/use-ink/ink/pull/2739) - Implements the API for the `pallet-revive` host functions `chain_id`, `balance_of`, `base_fee`, `origin`, `code_size`, `block_hash`, `block_author` - [#2719](https://github.com/use-ink/ink/pull/2719) - Implement `From` for "ink-as-dependency" contract refs - [#2728](https://github.com/use-ink/ink/pull/2728)