Skip to content
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
20f7188
Add basic implementation for total_supply and balance_of calls
dmitrylavrenov Aug 24, 2023
95481c1
Integrate initial precompile-evm-balances-erc20 implementation into h…
dmitrylavrenov Aug 24, 2023
abd9a18
Add metadata erc20 methods
dmitrylavrenov Aug 24, 2023
a24707d
Add approvals related logic
dmitrylavrenov Aug 24, 2023
5d3fa58
Add transfer related method
dmitrylavrenov Aug 24, 2023
d53e157
Add transferFrom related method
dmitrylavrenov Aug 24, 2023
34325d7
Add event logs
dmitrylavrenov Aug 24, 2023
440d8d0
Add docs to interface actions
dmitrylavrenov Aug 24, 2023
448bd57
Fix some clippy
dmitrylavrenov Aug 24, 2023
7c1e371
Add docs for approves logic
dmitrylavrenov Aug 24, 2023
b06c562
Fix typos
dmitrylavrenov Aug 24, 2023
5faec0f
Remove unnecesary docs
dmitrylavrenov Aug 25, 2023
64a9610
Introduce helper functions to simplify code
dmitrylavrenov Aug 25, 2023
70d0b08
Some refactoring
dmitrylavrenov Aug 25, 2023
5f81c84
Update features snapshot
dmitrylavrenov Aug 25, 2023
0d5b4d6
Fix docs
dmitrylavrenov Aug 25, 2023
41be202
Add ERC20 solidity interface
dmitrylavrenov Aug 25, 2023
6785a6e
Rename erc20 metadata at frontier precompiles
dmitrylavrenov Aug 25, 2023
c055a1a
Add mocked environment
dmitrylavrenov Aug 25, 2023
68de669
Add metadata related tests
dmitrylavrenov Aug 25, 2023
c7c1c34
Add balance_of_works and total_supply_works tests
dmitrylavrenov Aug 25, 2023
115acc3
Add approve_works test
dmitrylavrenov Aug 25, 2023
88b2356
Rename some tests
dmitrylavrenov Aug 25, 2023
8147024
Add transfer_works test
dmitrylavrenov Aug 25, 2023
e6a33ea
Add transfer_from_works test
dmitrylavrenov Aug 25, 2023
890f6a7
Rename symbol
dmitrylavrenov Aug 28, 2023
2992d29
Edit name erc20 metadata
dmitrylavrenov Aug 28, 2023
99d30dc
Introduce pallet-erc20 to store approvals data
dmitrylavrenov Aug 28, 2023
f0a31c8
Use approvals logic from pallet-erc20 at precompile
dmitrylavrenov Aug 28, 2023
9736378
Use currency config instead of pallet-evm-balances config
dmitrylavrenov Aug 28, 2023
350d670
Add erc20 related logic into pallet-erc20
dmitrylavrenov Aug 29, 2023
be70bdd
Add utility aliases
dmitrylavrenov Aug 30, 2023
aadf8b2
Integrate pallet-erc20 logic into precompile
dmitrylavrenov Aug 30, 2023
60f43cc
Fix mock at pallet-erc20
dmitrylavrenov Aug 30, 2023
c7421e3
Fix mock at precompile
dmitrylavrenov Aug 30, 2023
1fdbb26
Remove redundant dependency
dmitrylavrenov Aug 30, 2023
4b9a3e1
Rename pallet-erc20 to pallet-token-wrapper
dmitrylavrenov Aug 30, 2023
3aa99bd
Rename precompile-evm-balances-erc20 into precompile-token-wrapper
dmitrylavrenov Aug 30, 2023
9e5460e
Integrate introduced changes into humanode-runtime
dmitrylavrenov Aug 30, 2023
922f15f
Use wrapped-token instead for token-wrapper
dmitrylavrenov Aug 30, 2023
14883e3
Merge branch 'master' into precompile-balances-erc20
dmitrylavrenov Sep 4, 2023
9275197
Rename EvmBalancesErc20 into WrappedEvmBalances
dmitrylavrenov Sep 4, 2023
c52f822
Add total_supply_works test to pallet-wrapped-token
dmitrylavrenov Sep 4, 2023
9af708f
Improve approvals related test at pallet-wrapped-token
dmitrylavrenov Sep 4, 2023
03d9703
Add transfer_works test
dmitrylavrenov Sep 4, 2023
2a547f0
Add transfer_from_works test
dmitrylavrenov Sep 4, 2023
85a6c63
Add fails tests for transfer from logic
dmitrylavrenov Sep 4, 2023
2c1e87a
Add with_storage_layer usage at pallet-wrapped-token
dmitrylavrenov Sep 4, 2023
2a4ee15
Fix features
dmitrylavrenov Sep 6, 2023
654dc65
Use approvals aliases
dmitrylavrenov Sep 8, 2023
0f21930
Merge branch 'master' into precompile-balances-erc20
dmitrylavrenov Sep 8, 2023
cec5068
Rename pallet-wrapped-token into pallet-erc20-support
dmitrylavrenov Sep 8, 2023
87d81ff
Merge branch 'master' into precompile-balances-erc20
dmitrylavrenov Sep 11, 2023
38f68c2
Some renaming and docs improvements
dmitrylavrenov Sep 12, 2023
93be731
Update features snapshot
dmitrylavrenov Sep 12, 2023
0fdc379
Add comments for tests at pallet-erc20-support
dmitrylavrenov Sep 12, 2023
6e9c7db
Improve dispatch error handling
dmitrylavrenov Sep 12, 2023
7a6f1b5
Add more tests at precompile-erc20-support
dmitrylavrenov Sep 12, 2023
ad08491
Merge branch 'master' into precompile-balances-erc20
dmitrylavrenov Sep 14, 2023
38966f0
Merge branch 'master' into precompile-balances-erc20
dmitrylavrenov Sep 14, 2023
d66cddf
Rename precompile-erc20-support into precompile-native-currency
dmitrylavrenov Sep 14, 2023
78b3097
Rename Erc20EvmBalancesMetadata into EvmBalancesErc20Metadata
dmitrylavrenov Sep 14, 2023
974d188
Rename Erc20EvmBalances into EvmBalancesErc20Support
dmitrylavrenov Sep 14, 2023
66f9c50
Revert "Use approvals aliases"
dmitrylavrenov Sep 14, 2023
b293aad
Merge branch 'master' into precompile-balances-erc20
dmitrylavrenov Sep 14, 2023
276d52f
Properly handle transferFrom logic
dmitrylavrenov Sep 18, 2023
e600842
Improve dispatch error handling
dmitrylavrenov Sep 18, 2023
1354e9e
Add approve_overwrite_works test
dmitrylavrenov Sep 18, 2023
a7944d6
Fix transfer
dmitrylavrenov Sep 18, 2023
ebed3b0
Add tests to check approve logic with sending full approved balance
dmitrylavrenov Sep 18, 2023
00e1d79
Add approve_approval_value_more_than_balance_works test
dmitrylavrenov Sep 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/humanode-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pallet-bootnodes = { path = "../pallet-bootnodes", default-features = false }
pallet-chain-properties = { path = "../pallet-chain-properties", default-features = false }
pallet-chain-start-moment = { path = "../pallet-chain-start-moment", default-features = false }
pallet-currency-swap = { path = "../pallet-currency-swap", default-features = false }
pallet-erc20-support = { path = "../pallet-erc20-support", default-features = false }
pallet-ethereum-chain-id = { path = "../pallet-ethereum-chain-id", default-features = false }
pallet-evm-accounts-mapping = { path = "../pallet-evm-accounts-mapping", default-features = false }
pallet-humanode-session = { path = "../pallet-humanode-session", default-features = false }
Expand All @@ -31,6 +32,7 @@ pallet-token-claims = { path = "../pallet-token-claims", default-features = fals
pallet-vesting = { path = "../pallet-vesting", default-features = false }
precompile-bioauth = { path = "../precompile-bioauth", default-features = false }
precompile-currency-swap = { path = "../precompile-currency-swap", default-features = false }
precompile-erc20-support = { path = "../precompile-erc20-support", default-features = false }
precompile-evm-accounts-mapping = { path = "../precompile-evm-accounts-mapping", default-features = false }
precompile-utils = { path = "../precompile-utils", default-features = false }
primitives-auth-ticket = { path = "../primitives-auth-ticket", default-features = false }
Expand Down Expand Up @@ -164,6 +166,7 @@ std = [
"pallet-chain-properties/std",
"pallet-chain-start-moment/std",
"pallet-currency-swap/std",
"pallet-erc20-support/std",
"pallet-ethereum-chain-id/std",
"pallet-ethereum/std",
"pallet-evm-accounts-mapping/std",
Expand All @@ -189,6 +192,7 @@ std = [
"pallet-vesting/std",
"precompile-bioauth/std",
"precompile-currency-swap/std",
"precompile-erc20-support/std",
"precompile-evm-accounts-mapping/std",
"precompile-utils/std",
"primitives-auth-ticket/std",
Expand Down Expand Up @@ -234,6 +238,7 @@ try-runtime = [
"pallet-chain-properties/try-runtime",
"pallet-chain-start-moment/try-runtime",
"pallet-currency-swap/try-runtime",
"pallet-erc20-support/try-runtime",
"pallet-ethereum-chain-id/try-runtime",
"pallet-ethereum/try-runtime",
"pallet-evm-accounts-mapping/try-runtime",
Expand Down
13 changes: 11 additions & 2 deletions crates/humanode-runtime/src/frontier_precompiles.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use frame_support::traits::Currency;
use pallet_evm::{Precompile, PrecompileHandle, PrecompileResult, PrecompileSet};
use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
use precompile_bioauth::Bioauth;
use precompile_currency_swap::CurrencySwap;
use precompile_erc20_support::Erc20Support;
use precompile_evm_accounts_mapping::EvmAccountsMapping;
use sp_core::H160;
use sp_core::{H160, U256};
use sp_std::marker::PhantomData;

use crate::{currency_swap, AccountId, ConstU64, EvmAccountId};
Expand All @@ -23,7 +25,7 @@ where
R: pallet_evm::Config,
{
pub fn used_addresses() -> sp_std::vec::Vec<H160> {
sp_std::vec![1_u64, 2, 3, 4, 5, 1024, 1025, 2048, 2049, 2304]
sp_std::vec![1_u64, 2, 3, 4, 5, 1024, 1025, 2048, 2049, 2050, 2304]
.into_iter()
.map(hash)
.collect()
Expand All @@ -35,6 +37,12 @@ where
R: pallet_evm::Config,
R: pallet_bioauth::Config,
R: pallet_evm_accounts_mapping::Config,
R: pallet_evm_balances::Config,
R: pallet_erc20_support::Config,
<R as pallet_erc20_support::Config>::AccountId: From<H160>,
<<R as pallet_erc20_support::Config>::Currency as Currency<
<R as pallet_erc20_support::Config>::AccountId,
>>::Balance: Into<U256> + TryFrom<U256>,
R::ValidatorPublicKey: for<'a> TryFrom<&'a [u8]> + Eq,
{
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
Expand All @@ -51,6 +59,7 @@ where
// Humanode precompiles:
a if a == hash(2048) => Some(Bioauth::<R>::execute(handle)),
a if a == hash(2049) => Some(EvmAccountsMapping::<R>::execute(handle)),
a if a == hash(2050) => Some(Erc20Support::<R, ConstU64<200>>::execute(handle)),
a if a == hash(2304) => Some(CurrencySwap::<
currency_swap::EvmToNativeOneToOne,
EvmAccountId,
Expand Down
23 changes: 23 additions & 0 deletions crates/humanode-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,28 @@ impl pallet_balanced_currency_swap_bridges_initializer::Config for Runtime {
type WeightInfo = ();
}

pub struct Erc20EvmBalancesMetadata;

impl pallet_erc20_support::Metadata for Erc20EvmBalancesMetadata {
fn name() -> &'static str {
"Wrapped eHMND"
}

fn symbol() -> &'static str {
"WeHMND"
}

fn decimals() -> u8 {
18
}
}

impl pallet_erc20_support::Config for Runtime {
type AccountId = EvmAccountId;
type Currency = EvmBalances;
type Metadata = Erc20EvmBalancesMetadata;
}

// Create the runtime by composing the FRAME pallets that were previously
// configured.
construct_runtime!(
Expand Down Expand Up @@ -832,6 +854,7 @@ construct_runtime!(
EvmToNativeSwapBridgePot: pallet_pot::<Instance5> = 34,
CurrencySwap: pallet_currency_swap = 35,
BalancedCurrencySwapBridgesInitializer: pallet_balanced_currency_swap_bridges_initializer = 36,
Erc20EvmBalances: pallet_erc20_support = 37,
}
);

Expand Down
20 changes: 20 additions & 0 deletions crates/pallet-erc20-support/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "pallet-erc20-support"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
codec = { workspace = true, package = "parity-scale-codec", features = ["derive"] }
frame-support = { workspace = true }
frame-system = { workspace = true }
scale-info = { workspace = true, features = ["derive"] }

[dev-dependencies]
pallet-balances = { workspace = true }
sp-core = { workspace = true }

[features]
default = ["std"]
std = ["codec/std", "frame-support/std", "frame-system/std", "pallet-balances/std", "scale-info/std", "sp-core/std"]
try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime"]
172 changes: 172 additions & 0 deletions crates/pallet-erc20-support/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//! A substrate pallet that exposes currency instance using the ERC20 interface standard..

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
sp_runtime::{traits::CheckedSub, DispatchResult},
storage::with_storage_layer,
traits::{Currency, StorageVersion},
};
pub use pallet::*;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

/// Metadata of an ERC20 token.
pub trait Metadata {
/// Returns the name of the token.
fn name() -> &'static str;

/// Returns the symbol of the token.
fn symbol() -> &'static str;

/// Returns the decimals places of the token.
fn decimals() -> u8;
}

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);

/// Utility alias for easy access to the [`Config::AccountId`].
type AccountIdOf<T, I> = <T as Config<I>>::AccountId;

/// Utility alias for easy access to the [`Currency::Balance`] of the [`Config::Currency`] type.
type BalanceOf<T, I> = <<T as Config<I>>::Currency as Currency<AccountIdOf<T, I>>>::Balance;

// We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to
// fix them at their end.
#[allow(clippy::missing_docs_in_private_items)]
#[frame_support::pallet]
pub mod pallet {
use frame_support::{pallet_prelude::*, sp_runtime::traits::MaybeDisplay, sp_std::fmt::Debug};

use super::*;

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T, I = ()>(_);

/// Configuration trait of this pallet.
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
/// The user account identifier type.
type AccountId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Debug
+ MaybeDisplay
+ Ord
+ MaxEncodedLen;

/// The currency to be exposed as ERC20 token.
type Currency: Currency<AccountIdOf<Self, I>>;

/// Interface into ERC20 metadata implementation.
type Metadata: Metadata;
}

/// ERC20-style approvals data.
/// (Owner => Allowed => Amount).
#[pallet::storage]
#[pallet::getter(fn approvals)]
pub type Approvals<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
_,
Blake2_128Concat,
AccountIdOf<T, I>,
Blake2_128Concat,
AccountIdOf<T, I>,
BalanceOf<T, I>,
>;

/// Possible errors.
#[pallet::error]
pub enum Error<T, I = ()> {
/// Spender not allowed to transfer tokens on behalf of owner.
SpenderNotAllowed,
/// Spender can't transfer tokens more than allowed.
SpendMoreThanAllowed,
}
}

impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Returns the amount of tokens in existence.
pub fn total_supply() -> BalanceOf<T, I> {
T::Currency::total_issuance()
}

/// Returns the amount of tokens owned by provided account.
pub fn balance_of(owner: &AccountIdOf<T, I>) -> BalanceOf<T, I> {
T::Currency::total_balance(owner)
}

/// Returns the remaining number of tokens that spender will be allowed to spend on behalf of
/// owner. This is zero by default.
pub fn allowance(owner: &AccountIdOf<T, I>, spender: &AccountIdOf<T, I>) -> BalanceOf<T, I> {
Self::approvals(owner, spender).unwrap_or_default()
}

/// Sets amount as the allowance of spender over the caller’s tokens.
pub fn approve(owner: AccountIdOf<T, I>, spender: AccountIdOf<T, I>, amount: BalanceOf<T, I>) {
<Approvals<T, I>>::insert(owner, spender, amount);
}

/// Moves amount tokens from the caller’s account to recipient.
pub fn transfer(
caller: AccountIdOf<T, I>,
recipient: AccountIdOf<T, I>,
amount: BalanceOf<T, I>,
) -> DispatchResult {
with_storage_layer(move || {
T::Currency::transfer(
&caller,
&recipient,
amount,
frame_support::traits::ExistenceRequirement::AllowDeath,
)?;

Ok(())
})
}

/// Moves amount tokens from sender to recipient using the allowance mechanism,
/// amount is then deducted from the caller’s allowance.
pub fn transfer_from(
caller: AccountIdOf<T, I>,
sender: AccountIdOf<T, I>,
recipient: AccountIdOf<T, I>,
amount: BalanceOf<T, I>,
) -> DispatchResult {
with_storage_layer(move || {
// If caller is "sender", it can spend as much as it wants.
if caller != sender {
<Approvals<T, I>>::mutate(sender.clone(), caller, |entry| {
// Get current allowed value, exit if None.
let allowed = entry.ok_or(Error::<T, I>::SpenderNotAllowed)?;

// Remove "value" from allowed, exit if underflow.
let allowed = allowed
.checked_sub(&amount)
.ok_or(Error::<T, I>::SpendMoreThanAllowed)?;

// Update allowed value.
*entry = Some(allowed);

Ok::<(), Error<T, I>>(())
})?;
}

T::Currency::transfer(
&sender,
&recipient,
amount,
frame_support::traits::ExistenceRequirement::AllowDeath,
)?;

Ok(())
})
}
}
Loading