Skip to content

Commit 742c08c

Browse files
committed
Introduce the management of balances within Ethereum contracts.
1 parent 07eb8a7 commit 742c08c

File tree

15 files changed

+966
-470
lines changed

15 files changed

+966
-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
@@ -22,7 +22,7 @@ members = [
2222
]
2323

2424
[workspace.dependencies]
25-
alloy-primitives = { version = "1.0.0", default-features = false }
25+
alloy-primitives = { version = "1.3.0", default-features = false }
2626
alloy-sol-types = { version = "1.0.0", default-features = false }
2727
anyhow = "1.0.80"
2828
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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,25 @@ impl From<Amount> for U256 {
8686
}
8787
}
8888

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

1604+
use alloy_primitives::U256;
1605+
15851606
use super::Amount;
15861607

15871608
#[test]
@@ -1609,4 +1630,12 @@ mod tests {
16091630
format!("{:~^+9.1}", Amount::from_str("12.34").unwrap())
16101631
);
16111632
}
1633+
1634+
#[test]
1635+
fn test_conversion_amount_u256() {
1636+
let value_amount = Amount::from_tokens(15656565652209004332);
1637+
let value_u256: U256 = value_amount.into();
1638+
let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
1639+
assert_eq!(value_amount, value_amount_rev);
1640+
}
16121641
}

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 {
@@ -372,7 +380,12 @@ impl GenericApplicationId {
372380

373381
impl<A> From<ApplicationId<A>> for AccountOwner {
374382
fn from(app_id: ApplicationId<A>) -> Self {
375-
AccountOwner::Address32(app_id.application_description_hash)
383+
if app_id.is_evm() {
384+
let hash_bytes = app_id.application_description_hash.as_bytes();
385+
AccountOwner::Address20(hash_bytes[..20].try_into().unwrap())
386+
} else {
387+
AccountOwner::Address32(app_id.application_description_hash)
388+
}
376389
}
377390
}
378391

@@ -916,6 +929,14 @@ impl<A> ApplicationId<A> {
916929
}
917930
}
918931

932+
impl<A> ApplicationId<A> {
933+
/// Returns whether the `ApplicationId` is the one of an EVM application.
934+
pub fn is_evm(&self) -> bool {
935+
let bytes = self.application_description_hash.as_bytes();
936+
bytes.0[20..] == [0; 12]
937+
}
938+
}
939+
919940
#[cfg(with_revm)]
920941
impl<A> ApplicationId<A> {
921942
/// Converts the `ApplicationId` into an Ethereum Address.
@@ -929,18 +950,6 @@ impl<A> ApplicationId<A> {
929950
pub fn bytes32(&self) -> B256 {
930951
*self.application_description_hash.as_bytes()
931952
}
932-
933-
/// Returns whether the `ApplicationId` is the one of an EVM application.
934-
pub fn is_evm(&self) -> bool {
935-
let bytes = self.application_description_hash.as_bytes();
936-
let bytes = bytes.0.as_ref();
937-
for byte in &bytes[20..] {
938-
if byte != &0 {
939-
return false;
940-
}
941-
}
942-
true
943-
}
944953
}
945954

946955
#[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)