Skip to content

Commit 3b594da

Browse files
committed
Introduce the management of balances within Ethereum contracts.
1 parent 9cf38c1 commit 3b594da

File tree

15 files changed

+965
-470
lines changed

15 files changed

+965
-470
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ edition = "2021"
7373

7474
[workspace.dependencies]
7575
alloy = { version = "1.0.5", default-features = false }
76-
alloy-primitives = { version = "1.1.0", default-features = false, features = [
76+
alloy-primitives = { version = "1.3.0", default-features = false, features = [
7777
"serde",
7878
"k256",
7979
] }

examples/Cargo.lock

Lines changed: 16 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ members = [
2424
]
2525

2626
[workspace.dependencies]
27-
alloy-primitives = { version = "1.0.0", default-features = false }
27+
alloy-primitives = { version = "1.3.0", default-features = false }
2828
alloy-sol-types = { version = "1.0.0", default-features = false }
2929
anyhow = "1.0.80"
3030
assert_matches = "1.5.0"

examples/call-evm-counter/src/contract.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use alloy_sol_types::{sol, SolCall};
88
use call_evm_counter::{CallCounterAbi, CallCounterOperation};
99
use linera_sdk::{
1010
abis::evm::EvmAbi,
11-
linera_base_types::{ApplicationId, WithContractAbi},
11+
linera_base_types::{get_evm_mutation, Amount, ApplicationId, WithContractAbi},
1212
Contract, ContractRuntime,
1313
};
1414

@@ -55,15 +55,17 @@ impl Contract for CallCounterContract {
5555
}
5656
match operation {
5757
CallCounterOperation::Increment(increment) => {
58-
let operation = incrementCall { input: increment };
59-
let operation = operation.abi_encode();
60-
self.process_operation(operation)
58+
let mutation = incrementCall { input: increment };
59+
let mutation =
60+
get_evm_mutation(Amount::ZERO, mutation.abi_encode()).expect("A mutation");
61+
self.process_operation(mutation)
6162
}
6263
CallCounterOperation::TestCallAddress => {
6364
let remote_address = address!("0000000000000000000000000000000000000000");
64-
let operation = call_from_wasmCall { remote_address };
65-
let operation = operation.abi_encode();
66-
self.process_operation(operation)
65+
let mutation = call_from_wasmCall { remote_address };
66+
let mutation =
67+
get_evm_mutation(Amount::ZERO, mutation.abi_encode()).expect("A mutation");
68+
self.process_operation(mutation)
6769
}
6870
}
6971
}

linera-base/src/data_types.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@ impl From<Amount> for U256 {
8787
}
8888
}
8989

90+
/// Converting amount from `U256` to Amount can fail since
91+
/// Amount is a `u128`.
92+
#[derive(Error, Debug)]
93+
pub struct AmountConversionError(U256);
94+
95+
impl fmt::Display for AmountConversionError {
96+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97+
write!(f, "Amount conversion error for {}", self.0)
98+
}
99+
}
100+
101+
impl TryFrom<U256> for Amount {
102+
type Error = AmountConversionError;
103+
fn try_from(value: U256) -> Result<Amount, Self::Error> {
104+
let value = u128::try_from(&value).map_err(|_| AmountConversionError(value))?;
105+
Ok(Amount(value))
106+
}
107+
}
108+
90109
/// A block height to identify blocks in a chain.
91110
#[derive(
92111
Eq,
@@ -1596,6 +1615,7 @@ mod tests {
15961615

15971616
use super::{Amount, BlobContent};
15981617
use crate::identifiers::BlobType;
1618+
use alloy_primitives::U256;
15991619

16001620
#[test]
16011621
fn display_amount() {
@@ -1653,4 +1673,12 @@ mod tests {
16531673
assert_eq!(hash1, hash2, "Hashes should be equal for same content");
16541674
assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
16551675
}
1676+
1677+
#[test]
1678+
fn test_conversion_amount_u256() {
1679+
let value_amount = Amount::from_tokens(15656565652209004332);
1680+
let value_u256: U256 = value_amount.into();
1681+
let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
1682+
assert_eq!(value_amount, value_amount_rev);
1683+
}
16561684
}

linera-base/src/identifiers.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ impl AccountOwner {
7070
}
7171
}
7272

73+
#[cfg(with_revm)]
74+
impl From<Address> for AccountOwner {
75+
fn from(address: Address) -> Self {
76+
let address = address.into_array();
77+
AccountOwner::Address20(address)
78+
}
79+
}
80+
7381
#[cfg(with_testing)]
7482
impl From<CryptoHash> for AccountOwner {
7583
fn from(address: CryptoHash) -> Self {
@@ -407,7 +415,12 @@ impl GenericApplicationId {
407415

408416
impl<A> From<ApplicationId<A>> for AccountOwner {
409417
fn from(app_id: ApplicationId<A>) -> Self {
410-
AccountOwner::Address32(app_id.application_description_hash)
418+
if app_id.is_evm() {
419+
let hash_bytes = app_id.application_description_hash.as_bytes();
420+
AccountOwner::Address20(hash_bytes[..20].try_into().unwrap())
421+
} else {
422+
AccountOwner::Address32(app_id.application_description_hash)
423+
}
411424
}
412425
}
413426

@@ -951,6 +964,14 @@ impl<A> ApplicationId<A> {
951964
}
952965
}
953966

967+
impl<A> ApplicationId<A> {
968+
/// Returns whether the `ApplicationId` is the one of an EVM application.
969+
pub fn is_evm(&self) -> bool {
970+
let bytes = self.application_description_hash.as_bytes();
971+
bytes.0[20..] == [0; 12]
972+
}
973+
}
974+
954975
#[cfg(with_revm)]
955976
impl<A> ApplicationId<A> {
956977
/// Converts the `ApplicationId` into an Ethereum Address.
@@ -964,18 +985,6 @@ impl<A> ApplicationId<A> {
964985
pub fn bytes32(&self) -> B256 {
965986
*self.application_description_hash.as_bytes()
966987
}
967-
968-
/// Returns whether the `ApplicationId` is the one of an EVM application.
969-
pub fn is_evm(&self) -> bool {
970-
let bytes = self.application_description_hash.as_bytes();
971-
let bytes = bytes.0.as_ref();
972-
for byte in &bytes[20..] {
973-
if byte != &0 {
974-
return false;
975-
}
976-
}
977-
true
978-
}
979988
}
980989

981990
#[derive(Serialize, Deserialize)]

linera-base/src/vm.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use linera_witty::{WitLoad, WitStore, WitType};
1111
use serde::{Deserialize, Serialize};
1212
use thiserror::Error;
1313

14+
use crate::data_types::Amount;
15+
1416
#[derive(
1517
Clone,
1618
Copy,
@@ -65,3 +67,32 @@ pub enum EvmQuery {
6567
/// A request to schedule an operation that can mutate the application state.
6668
Mutation(Vec<u8>),
6769
}
70+
71+
/// An EVM mutation containing a value and argument data
72+
#[derive(Default, Serialize, Deserialize)]
73+
pub struct EvmMutation {
74+
/// The amount being transferred
75+
pub value: alloy_primitives::U256,
76+
/// The encoded argument data
77+
pub argument: Vec<u8>,
78+
}
79+
80+
/// Creates an internal mutation from value and argument data
81+
pub fn get_evm_mutation(
82+
amount: crate::data_types::Amount,
83+
argument: Vec<u8>,
84+
) -> Result<Vec<u8>, bcs::Error> {
85+
let value = amount.into();
86+
let evm_mutation = EvmMutation { value, argument };
87+
bcs::to_bytes(&evm_mutation)
88+
}
89+
90+
/// The instantiation argument to EVM smart contracts.
91+
/// `value` is the amount being transferred.
92+
#[derive(Default, Serialize, Deserialize)]
93+
pub struct EvmInstantiation {
94+
/// The initial value put in the instantiation of the contract.
95+
pub value: Amount,
96+
/// The input to the `fn instantiate` of the EVM smart contract.
97+
pub argument: Vec<u8>,
98+
}

0 commit comments

Comments
 (0)