Skip to content

Commit f2e0c9f

Browse files
committed
Introduce the management of balances within Ethereum contracts.
1 parent 88768dc commit f2e0c9f

File tree

20 files changed

+1162
-522
lines changed

20 files changed

+1162
-522
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
@@ -25,7 +25,7 @@ members = [
2525
]
2626

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

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

Lines changed: 5 additions & 3 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::{Amount, ApplicationId, EvmOperation, WithContractAbi},
1212
Contract, ContractRuntime,
1313
};
1414

@@ -56,13 +56,15 @@ impl Contract for CallCounterContract {
5656
match operation {
5757
CallCounterOperation::Increment(increment) => {
5858
let operation = incrementCall { input: increment };
59-
let operation = operation.abi_encode();
59+
let operation = EvmOperation::new(Amount::ZERO, operation.abi_encode());
60+
let operation = operation.to_bytes().unwrap();
6061
self.process_operation(operation)
6162
}
6263
CallCounterOperation::TestCallAddress => {
6364
let remote_address = address!("0000000000000000000000000000000000000000");
6465
let operation = call_from_wasmCall { remote_address };
65-
let operation = operation.abi_encode();
66+
let operation = EvmOperation::new(Amount::ZERO, operation.abi_encode());
67+
let operation = operation.to_bytes().unwrap();
6668
self.process_operation(operation)
6769
}
6870
}

linera-base/src/data_types.rs

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

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

1628+
use alloy_primitives::U256;
1629+
16091630
use super::{Amount, BlobContent};
16101631
use crate::identifiers::BlobType;
16111632

@@ -1665,4 +1686,12 @@ mod tests {
16651686
assert_eq!(hash1, hash2, "Hashes should be equal for same content");
16661687
assert_eq!(blob1.bytes(), blob2.bytes(), "Byte content should be equal");
16671688
}
1689+
1690+
#[test]
1691+
fn test_conversion_amount_u256() {
1692+
let value_amount = Amount::from_tokens(15656565652209004332);
1693+
let value_u256: U256 = value_amount.into();
1694+
let value_amount_rev = Amount::try_from(value_u256).expect("Failed conversion");
1695+
assert_eq!(value_amount, value_amount_rev);
1696+
}
16681697
}

linera-base/src/identifiers.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ impl AccountOwner {
7979
}
8080
}
8181

82+
#[cfg(with_revm)]
83+
impl From<Address> for AccountOwner {
84+
fn from(address: Address) -> Self {
85+
let address = address.into_array();
86+
AccountOwner::Address20(address)
87+
}
88+
}
89+
8290
#[cfg(with_testing)]
8391
impl From<CryptoHash> for AccountOwner {
8492
fn from(address: CryptoHash) -> Self {
@@ -416,7 +424,12 @@ impl GenericApplicationId {
416424

417425
impl<A> From<ApplicationId<A>> for AccountOwner {
418426
fn from(app_id: ApplicationId<A>) -> Self {
419-
AccountOwner::Address32(app_id.application_description_hash)
427+
if app_id.is_evm() {
428+
let hash_bytes = app_id.application_description_hash.as_bytes();
429+
AccountOwner::Address20(hash_bytes[..20].try_into().unwrap())
430+
} else {
431+
AccountOwner::Address32(app_id.application_description_hash)
432+
}
420433
}
421434
}
422435

@@ -960,6 +973,14 @@ impl<A> ApplicationId<A> {
960973
}
961974
}
962975

976+
impl<A> ApplicationId<A> {
977+
/// Returns whether the `ApplicationId` is the one of an EVM application.
978+
pub fn is_evm(&self) -> bool {
979+
let bytes = self.application_description_hash.as_bytes();
980+
bytes.0[20..] == [0; 12]
981+
}
982+
}
983+
963984
#[cfg(with_revm)]
964985
impl From<Address> for ApplicationId {
965986
fn from(address: Address) -> ApplicationId {
@@ -985,18 +1006,6 @@ impl<A> ApplicationId<A> {
9851006
pub fn bytes32(&self) -> B256 {
9861007
*self.application_description_hash.as_bytes()
9871008
}
988-
989-
/// Returns whether the `ApplicationId` is the one of an EVM application.
990-
pub fn is_evm(&self) -> bool {
991-
let bytes = self.application_description_hash.as_bytes();
992-
let bytes = bytes.0.as_ref();
993-
for byte in &bytes[20..] {
994-
if byte != &0 {
995-
return false;
996-
}
997-
}
998-
true
999-
}
10001009
}
10011010

10021011
#[derive(Serialize, Deserialize)]
@@ -1261,4 +1270,18 @@ mod tests {
12611270
let stream_id2 = StreamId::from_str(&format!("{stream_id1}")).unwrap();
12621271
assert_eq!(stream_id1, stream_id2);
12631272
}
1273+
1274+
#[cfg(with_revm)]
1275+
#[test]
1276+
fn test_address_account_owner() {
1277+
use alloy_primitives::Address;
1278+
let mut vec = Vec::new();
1279+
for i in 0..20 {
1280+
vec.push(i as u8);
1281+
}
1282+
let address1 = Address::from_slice(&vec);
1283+
let account_owner = AccountOwner::from(address1);
1284+
let address2 = account_owner.to_evm_address().unwrap();
1285+
assert_eq!(address1, address2);
1286+
}
12641287
}

linera-base/src/vm.rs

Lines changed: 39 additions & 1 deletion
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,
@@ -63,5 +65,41 @@ pub enum EvmQuery {
6365
/// A read-only query.
6466
Query(Vec<u8>),
6567
/// A request to schedule an operation that can mutate the application state.
66-
Mutation(Vec<u8>),
68+
Operation(Vec<u8>),
69+
}
70+
71+
/// An EVM operation containing a value and argument data.
72+
#[derive(Debug, Default, Serialize, Deserialize)]
73+
pub struct EvmOperation {
74+
/// The amount being transferred.
75+
pub value: alloy_primitives::U256,
76+
/// The encoded argument data.
77+
pub argument: Vec<u8>,
78+
}
79+
80+
impl EvmOperation {
81+
pub fn new(amount: Amount, argument: Vec<u8>) -> Self {
82+
Self {
83+
value: amount.into(),
84+
argument,
85+
}
86+
}
87+
88+
pub fn to_bytes(&self) -> Result<Vec<u8>, bcs::Error> {
89+
bcs::to_bytes(&self)
90+
}
91+
92+
pub fn to_evm_query(&self) -> Result<EvmQuery, bcs::Error> {
93+
Ok(EvmQuery::Operation(self.to_bytes()?))
94+
}
95+
}
96+
97+
/// The instantiation argument to EVM smart contracts.
98+
/// `value` is the amount being transferred.
99+
#[derive(Default, Serialize, Deserialize)]
100+
pub struct EvmInstantiation {
101+
/// The initial value put in the instantiation of the contract.
102+
pub value: alloy_primitives::U256,
103+
/// The input to the `fn instantiate` of the EVM smart contract.
104+
pub argument: Vec<u8>,
67105
}

0 commit comments

Comments
 (0)