-
Notifications
You must be signed in to change notification settings - Fork 665
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: eth wallet contract implementation
- Loading branch information
Showing
30 changed files
with
8,434 additions
and
1,612 deletions.
There are no files selected for viewing
5,173 changes: 5,173 additions & 0 deletions
5,173
runtime/near-wallet-contract/implementation/Cargo.lock
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
[workspace.package] | ||
authors = ["Aurora Labs <[email protected]>"] | ||
version = "0.1.0" | ||
edition = "2021" | ||
homepage = "https://github.com/aurora-is-near/eth-wallet-contract" | ||
repository = "https://github.com/aurora-is-near/eth-wallet-contract" | ||
license = "CC0-1.0" | ||
|
||
|
||
[workspace.dependencies] | ||
aurora-engine-transactions = { git = "https://github.com/aurora-is-near/aurora-engine.git", rev = "c03a2d8610cd27a9decb91b3bddb107db2177b29", default-features = false, features = ["contract"]} | ||
base64 = "0.21" | ||
ethabi = { version = "18", default-features = false } | ||
hex = "0.4" | ||
near-sdk = { version = "5.0" } | ||
once_cell = "1.18" | ||
serde = { version = "1", features = ["derive"] } | ||
|
||
# dev-dependencies | ||
anyhow = "1" | ||
aurora-engine-types = { git = "https://github.com/aurora-is-near/aurora-engine.git", rev = "c03a2d8610cd27a9decb91b3bddb107db2177b29", default-features = false } | ||
near-crypto = "0.21" | ||
near-workspaces = "0.10" | ||
rlp = { version = "0.5", default-features = false } | ||
serde_json = "1" | ||
sha3 = "0.10" | ||
tokio = { version = "1", features = ["full"] } | ||
|
||
[workspace] | ||
resolver = "2" | ||
members = [ | ||
"address-registrar", | ||
"wallet-contract", | ||
] | ||
|
||
[profile.release] | ||
panic = 'abort' |
15 changes: 15 additions & 0 deletions
15
runtime/near-wallet-contract/implementation/address-registrar/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "eth-address-registrar" | ||
version.workspace = true | ||
edition.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
license.workspace = true | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[dependencies] | ||
hex.workspace = true | ||
near-sdk.workspace = true | ||
serde.workspace = true |
76 changes: 76 additions & 0 deletions
76
runtime/near-wallet-contract/implementation/address-registrar/src/lib.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
use near_sdk::{ | ||
borsh::{BorshDeserialize, BorshSerialize}, | ||
env, near_bindgen, | ||
store::{lookup_map::Entry, LookupMap}, | ||
AccountId, BorshStorageKey, PanicOnDefault, | ||
}; | ||
|
||
type Address = [u8; 20]; | ||
|
||
#[derive(BorshSerialize, BorshStorageKey)] | ||
#[borsh(crate = "near_sdk::borsh")] | ||
enum StorageKey { | ||
Addresses, | ||
} | ||
|
||
#[near_bindgen] | ||
#[derive(PanicOnDefault, BorshDeserialize, BorshSerialize)] | ||
#[borsh(crate = "near_sdk::borsh")] | ||
pub struct AddressRegistrar { | ||
pub addresses: LookupMap<Address, AccountId>, | ||
} | ||
|
||
#[near_bindgen] | ||
impl AddressRegistrar { | ||
#[init] | ||
pub fn new() -> Self { | ||
Self { | ||
addresses: LookupMap::new(StorageKey::Addresses), | ||
} | ||
} | ||
|
||
pub fn register(&mut self, account_id: AccountId) -> Option<String> { | ||
let address = account_id_to_address(&account_id); | ||
|
||
match self.addresses.entry(address) { | ||
Entry::Vacant(entry) => { | ||
let address = format!("0x{}", hex::encode(address)); | ||
let log_message = format!("Added entry {} -> {}", address, account_id); | ||
entry.insert(account_id); | ||
env::log_str(&log_message); | ||
Some(address) | ||
} | ||
Entry::Occupied(entry) => { | ||
let log_message = format!( | ||
"Address collision between {} and {}. Keeping the former.", | ||
entry.get(), | ||
account_id | ||
); | ||
env::log_str(&log_message); | ||
None | ||
} | ||
} | ||
} | ||
|
||
pub fn lookup(&self, address: String) -> Option<AccountId> { | ||
let address = { | ||
let mut buf = [0u8; 20]; | ||
hex::decode_to_slice(address.strip_prefix("0x").unwrap_or(&address), &mut buf) | ||
.unwrap_or_else(|_| env::panic_str("Invalid hex encoding")); | ||
buf | ||
}; | ||
self.addresses.get(&address).cloned() | ||
} | ||
|
||
pub fn get_address(&self, account_id: AccountId) -> String { | ||
let address = account_id_to_address(&account_id); | ||
format!("0x{}", hex::encode(address)) | ||
} | ||
} | ||
|
||
fn account_id_to_address(account_id: &AccountId) -> Address { | ||
let hash = near_sdk::env::keccak256_array(account_id.as_bytes()); | ||
let mut result = [0u8; 20]; | ||
result.copy_from_slice(&hash[12..32]); | ||
result | ||
} |
29 changes: 29 additions & 0 deletions
29
runtime/near-wallet-contract/implementation/wallet-contract/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[package] | ||
name = "eth-wallet-contract" | ||
version.workspace = true | ||
edition.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
license.workspace = true | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[dependencies] | ||
aurora-engine-transactions.workspace = true | ||
base64.workspace = true | ||
ethabi.workspace = true | ||
hex.workspace = true | ||
near-sdk.workspace = true | ||
once_cell.workspace = true | ||
serde.workspace = true | ||
serde_json.workspace = true | ||
|
||
[dev-dependencies] | ||
anyhow.workspace = true | ||
aurora-engine-types.workspace = true | ||
near-crypto.workspace = true | ||
near-workspaces.workspace = true | ||
rlp.workspace = true | ||
sha3.workspace = true | ||
tokio.workspace = true |
1 change: 1 addition & 0 deletions
1
runtime/near-wallet-contract/implementation/wallet-contract/src/ADDRESS_REGISTRAR_ACCOUNT_ID
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
todo |
167 changes: 167 additions & 0 deletions
167
runtime/near-wallet-contract/implementation/wallet-contract/src/error.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
use std::fmt; | ||
|
||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
pub enum Error { | ||
AccountNonceExhausted, | ||
AccountId(AccountIdError), | ||
Relayer(RelayerError), | ||
User(UserError), | ||
Caller(CallerError), | ||
} | ||
|
||
/// Errors that should never happen when the Eth Implicit accounts feature | ||
/// is available on Near. These errors relate to parsing a 20-byte address | ||
/// from a Near account ID. | ||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
pub enum AccountIdError { | ||
AccountIdTooShort, | ||
Missing0xPrefix, | ||
InvalidHex, | ||
} | ||
|
||
/// Errors which should never happen if the relayer is honest. | ||
/// If these errors happen then we should ban the relayer (revoke their access key). | ||
/// An external caller (as opposed to a relayer with a Function Call access key) may | ||
/// also trigger these errors by passing bad arguments, but this is not an issue | ||
/// (there is no ban list for external callers) because they are paying the gas fees | ||
/// for their own mistakes. | ||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
pub enum RelayerError { | ||
/// Relayers should always check the nonce before sending | ||
InvalidNonce, | ||
/// Relayers should always encode the transaction correctly | ||
InvalidBase64, | ||
/// Relayers should always send valid transactions | ||
TxParsing(aurora_engine_transactions::Error), | ||
/// Relayers should always send correctly signed transactions | ||
InvalidSender, | ||
/// Relayers should always give the correct target account | ||
InvalidTarget, | ||
/// Relayers should always check the transaction is signed with the correct chain id. | ||
InvalidChainId, | ||
} | ||
|
||
/// Errors that arise from problems in the data signed by the user | ||
/// (i.e. in the Ethereum transaction itself). A careful power-user | ||
/// should never see these errors because they can review the data | ||
/// they are signing. If a user does see these errors then there is | ||
/// likely a bug in the front-end code that is constructing the Ethereum | ||
/// transaction to be signed. | ||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
pub enum UserError { | ||
EvmDeployDisallowed, | ||
ValueTooLarge, | ||
UnknownPublicKeyKind, | ||
InvalidEd25519Key, | ||
InvalidSecp256k1Key, | ||
InvalidAccessKeyAccountId, | ||
UnsupportedAction(UnsupportedAction), | ||
UnknownFunctionSelector, | ||
InvalidAbiEncodedData, | ||
ExcessYoctoNear, | ||
} | ||
|
||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
pub enum UnsupportedAction { | ||
AddFullAccessKey, | ||
CreateAccount, | ||
Delegate, | ||
DeleteAccount, | ||
DeployContract, | ||
Stake, | ||
} | ||
|
||
/// Errors that arise from external accounts calling the Wallet Contract. | ||
/// The `rlp_execute` function is intentionally public so that any account | ||
/// can pay for the fees on behalf of a Wallet Contract key holder. | ||
/// These errors are not a big deal from the perspective of the Wallet Contract | ||
/// because the cost for executing such erroneous transactions are paid for | ||
/// by that external caller. | ||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
pub enum CallerError { | ||
InsufficientAttachedValue, | ||
} | ||
|
||
impl From<aurora_engine_transactions::Error> for Error { | ||
fn from(value: aurora_engine_transactions::Error) -> Self { | ||
Self::Relayer(RelayerError::TxParsing(value)) | ||
} | ||
} | ||
|
||
impl fmt::Display for AccountIdError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::AccountIdTooShort => f.write_str("Error: account ID too short"), | ||
Self::Missing0xPrefix => f.write_str("Error: account ID missing 0x"), | ||
Self::InvalidHex => f.write_str("Error: account ID is not valid hex encoding"), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for RelayerError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::TxParsing(e) => std::write!(f, "Error parsing RLP {}", e.as_str()), | ||
Self::InvalidSender => f.write_str("Error: signature is not from account owner"), | ||
Self::InvalidBase64 => f.write_str("Error: invalid base64 encoding"), | ||
Self::InvalidTarget => { | ||
f.write_str("Error: target does not match to in signed transaction") | ||
} | ||
Self::InvalidNonce => f.write_str("Error: invalid nonce value"), | ||
Self::InvalidChainId => f.write_str("Error: invalid chain id value"), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for UserError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::EvmDeployDisallowed => { | ||
f.write_str("Error: transactions deploying EVM contracts not allowed") | ||
} | ||
Self::ValueTooLarge => { | ||
f.write_str("Error: transaction value must be representable by 128 bits") | ||
} | ||
Self::UnknownPublicKeyKind => f.write_str("Error: unknown public key kind"), | ||
Self::InvalidEd25519Key => f.write_str("Error: invalid ED25519 public key"), | ||
Self::InvalidSecp256k1Key => f.write_str("Error: invalid SECP256k1 public key"), | ||
Self::InvalidAccessKeyAccountId => f.write_str( | ||
"Error: attempt to add function call access key with invalid account id", | ||
), | ||
Self::UnsupportedAction(a) => { | ||
std::write!(f, "Error unsupported action {:?}", a) | ||
} | ||
Self::UnknownFunctionSelector => f.write_str("Error: unknown function selector"), | ||
Self::InvalidAbiEncodedData => { | ||
f.write_str("Error: invalid ABI encoding in transaction data") | ||
} | ||
Self::ExcessYoctoNear => f.write_str( | ||
"Error: only at most 1_000_000 yoctoNear can be included directly in an action", | ||
), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for CallerError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::InsufficientAttachedValue => { | ||
f.write_str("Error: external calls must attach Near to pay for their transactions") | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for Error { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::AccountNonceExhausted => f.write_str("Error: no nonce values remain"), | ||
Self::AccountId(e) => e.fmt(f), | ||
Self::Relayer(e) => e.fmt(f), | ||
Self::User(e) => e.fmt(f), | ||
Self::Caller(e) => e.fmt(f), | ||
} | ||
} | ||
} | ||
|
||
impl std::error::Error for Error {} |
Oops, something went wrong.