diff --git a/Cargo.lock b/Cargo.lock
index 7822e3d5..cee5c7a3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7740,23 +7740,54 @@ name = "pallet-evm-precompile-multi-asset-delegation"
version = "0.1.0"
dependencies = [
"derive_more 1.0.0",
+ "ethabi",
+ "ethereum",
+ "ethers",
+ "fp-account",
+ "fp-consensus",
+ "fp-dynamic-fee",
+ "fp-ethereum",
"fp-evm",
+ "fp-rpc",
+ "fp-self-contained",
+ "fp-storage",
+ "frame-election-provider-support",
"frame-support",
"frame-system",
+ "hex",
"hex-literal 0.4.1",
+ "libsecp256k1",
+ "num_enum",
"pallet-assets",
"pallet-balances",
+ "pallet-base-fee",
+ "pallet-dynamic-fee",
+ "pallet-ethereum",
"pallet-evm",
+ "pallet-evm-chain-id",
+ "pallet-evm-precompile-blake2",
+ "pallet-evm-precompile-bn128",
+ "pallet-evm-precompile-curve25519",
+ "pallet-evm-precompile-ed25519",
+ "pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-sha3fips",
+ "pallet-evm-precompile-simple",
"pallet-multi-asset-delegation",
+ "pallet-session",
+ "pallet-staking",
"pallet-timestamp",
"parity-scale-codec",
"precompile-utils",
"scale-info",
"serde",
+ "serde_json",
"sha3",
+ "smallvec",
"sp-core",
"sp-io",
+ "sp-keystore",
"sp-runtime",
+ "sp-staking",
"sp-std",
"tangle-primitives",
]
@@ -8282,16 +8313,56 @@ dependencies = [
name = "pallet-multi-asset-delegation"
version = "1.2.3"
dependencies = [
+ "ethabi",
+ "ethereum",
+ "ethers",
+ "fp-account",
+ "fp-consensus",
+ "fp-dynamic-fee",
+ "fp-ethereum",
+ "fp-evm",
+ "fp-rpc",
+ "fp-self-contained",
+ "fp-storage",
"frame-benchmarking",
+ "frame-election-provider-support",
"frame-support",
"frame-system",
+ "hex",
+ "hex-literal 0.4.1",
+ "itertools 0.13.0",
+ "libsecp256k1",
+ "log",
+ "num_enum",
"pallet-assets",
"pallet-balances",
+ "pallet-base-fee",
+ "pallet-dynamic-fee",
+ "pallet-ethereum",
+ "pallet-evm",
+ "pallet-evm-chain-id",
+ "pallet-evm-precompile-blake2",
+ "pallet-evm-precompile-bn128",
+ "pallet-evm-precompile-curve25519",
+ "pallet-evm-precompile-ed25519",
+ "pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-sha3fips",
+ "pallet-evm-precompile-simple",
+ "pallet-session",
+ "pallet-staking",
+ "pallet-timestamp",
"parity-scale-codec",
+ "precompile-utils",
"scale-info",
+ "serde",
+ "serde_json",
+ "smallvec",
"sp-core",
"sp-io",
+ "sp-keyring",
+ "sp-keystore",
"sp-runtime",
+ "sp-staking",
"sp-std",
"tangle-primitives",
]
@@ -14231,7 +14302,9 @@ dependencies = [
"ark-std",
"educe 0.6.0",
"ethabi",
+ "fp-evm",
"frame-support",
+ "frame-system",
"hex",
"log",
"parity-scale-codec",
diff --git a/client/evm-tracing/src/formatters/blockscout.rs b/client/evm-tracing/src/formatters/blockscout.rs
index fa8611bb..61b8033e 100644
--- a/client/evm-tracing/src/formatters/blockscout.rs
+++ b/client/evm-tracing/src/formatters/blockscout.rs
@@ -14,11 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see .
-use crate::listeners::call_list::Listener;
-use crate::types::serialization::*;
-use crate::types::{
- single::{Call, TransactionTrace},
- CallResult, CallType, CreateResult,
+use crate::{
+ listeners::call_list::Listener,
+ types::{
+ serialization::*,
+ single::{Call, TransactionTrace},
+ CallResult, CallType, CreateResult,
+ },
};
use ethereum_types::{H160, U256};
use parity_scale_codec::{Decode, Encode};
diff --git a/client/evm-tracing/src/formatters/call_tracer.rs b/client/evm-tracing/src/formatters/call_tracer.rs
index b0451b0b..6b2d7ae4 100644
--- a/client/evm-tracing/src/formatters/call_tracer.rs
+++ b/client/evm-tracing/src/formatters/call_tracer.rs
@@ -154,7 +154,7 @@ impl super::ResponseFormatter for Formatter {
//
// We consider an item to be `Ordering::Less` when:
// - Is closer to the root or
- // - Is greater than its sibling.
+ // - Is greater than its sibling.
result.sort_by(|a, b| match (a, b) {
(
Call::CallTracer(CallTracerCall { trace_address: Some(a), .. }),
diff --git a/client/evm-tracing/src/formatters/raw.rs b/client/evm-tracing/src/formatters/raw.rs
index d3588e2f..30e35242 100644
--- a/client/evm-tracing/src/formatters/raw.rs
+++ b/client/evm-tracing/src/formatters/raw.rs
@@ -14,8 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see .
-use crate::listeners::raw::Listener;
-use crate::types::single::TransactionTrace;
+use crate::{listeners::raw::Listener, types::single::TransactionTrace};
pub struct Formatter;
diff --git a/client/evm-tracing/src/formatters/trace_filter.rs b/client/evm-tracing/src/formatters/trace_filter.rs
index 12266cb7..9bb03210 100644
--- a/client/evm-tracing/src/formatters/trace_filter.rs
+++ b/client/evm-tracing/src/formatters/trace_filter.rs
@@ -15,12 +15,15 @@
// along with Moonbeam. If not, see .
use super::blockscout::BlockscoutCallInner as CallInner;
-use crate::listeners::call_list::Listener;
-use crate::types::{
- block::{
- TransactionTrace, TransactionTraceAction, TransactionTraceOutput, TransactionTraceResult,
+use crate::{
+ listeners::call_list::Listener,
+ types::{
+ block::{
+ TransactionTrace, TransactionTraceAction, TransactionTraceOutput,
+ TransactionTraceResult,
+ },
+ CallResult, CreateResult, CreateType,
},
- CallResult, CreateResult, CreateType,
};
use ethereum_types::H256;
diff --git a/client/evm-tracing/src/listeners/call_list.rs b/client/evm-tracing/src/listeners/call_list.rs
index dac7c1fb..9da8a66a 100644
--- a/client/evm-tracing/src/listeners/call_list.rs
+++ b/client/evm-tracing/src/listeners/call_list.rs
@@ -14,9 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see .
-use crate::formatters::blockscout::BlockscoutCall as Call;
-use crate::formatters::blockscout::BlockscoutCallInner as CallInner;
-use crate::types::{single::Log, CallResult, CallType, ContextType, CreateResult};
+use crate::{
+ formatters::blockscout::{BlockscoutCall as Call, BlockscoutCallInner as CallInner},
+ types::{single::Log, CallResult, CallType, ContextType, CreateResult},
+};
use ethereum_types::{H160, U256};
use evm_tracing_events::{
runtime::{Capture, ExitError, ExitReason, ExitSucceed},
@@ -1094,7 +1095,8 @@ mod tests {
listener.finish_transaction();
assert_eq!(listener.entries.len(), 1);
// Each nested call contains 11 elements in the callstack (main + 10 subcalls).
- // There are 5 main nested calls for a total of 56 elements in the callstack: 1 main + 55 nested.
+ // There are 5 main nested calls for a total of 56 elements in the callstack: 1 main + 55
+ // nested.
assert_eq!(listener.entries[0].len(), (depth * (subdepth + 1)) + 1);
}
diff --git a/client/evm-tracing/src/listeners/raw.rs b/client/evm-tracing/src/listeners/raw.rs
index 180fd36e..483ea0a3 100644
--- a/client/evm-tracing/src/listeners/raw.rs
+++ b/client/evm-tracing/src/listeners/raw.rs
@@ -243,7 +243,8 @@ impl Listener {
.global_storage_changes
.insert(context.address, context.storage_cache);
- // Apply storage changes to parent, either updating its cache or map of changes.
+ // Apply storage changes to parent, either updating its cache or
+ // map of changes.
for (address, mut storage) in
context.global_storage_changes.into_iter()
{
diff --git a/client/rpc/trace/src/lib.rs b/client/rpc/trace/src/lib.rs
index a62e1794..72c564df 100644
--- a/client/rpc/trace/src/lib.rs
+++ b/client/rpc/trace/src/lib.rs
@@ -21,8 +21,8 @@
//! The implementation is composed of multiple tasks :
//! - Many calls the RPC handler `Trace::filter`, communicating with the main task.
//! - A main `CacheTask` managing the cache and the communication between tasks.
-//! - For each traced block an async task responsible to wait for a permit, spawn a blocking
-//! task and waiting for the result, then send it to the main `CacheTask`.
+//! - For each traced block an async task responsible to wait for a permit, spawn a blocking task
+//! and waiting for the result, then send it to the main `CacheTask`.
use futures::{select, stream::FuturesUnordered, FutureExt, StreamExt};
use std::{collections::BTreeMap, future::Future, marker::PhantomData, sync::Arc, time::Duration};
@@ -595,9 +595,9 @@ where
/// Handle a request to get the traces of the provided block.
/// - If the result is stored in the cache, it sends it immediatly.
- /// - If the block is currently being pooled, it is added in this block cache waiting list,
- /// and all requests concerning this block will be satisfied when the tracing for this block
- /// is finished.
+ /// - If the block is currently being pooled, it is added in this block cache waiting list, and
+ /// all requests concerning this block will be satisfied when the tracing for this block is
+ /// finished.
/// - If this block is missing from the cache, it means no batch asked for it. All requested
/// blocks should be contained in a batch beforehand, and thus an error is returned.
#[instrument(skip(self))]
diff --git a/node/src/manual_seal.rs b/node/src/manual_seal.rs
index 8ee14591..d1edecd0 100644
--- a/node/src/manual_seal.rs
+++ b/node/src/manual_seal.rs
@@ -14,33 +14,30 @@
// limitations under the License.
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
-use crate::cli::Sealing;
pub use crate::eth::{db_config_dir, EthConfiguration};
-use crate::eth::{
- new_frontier_partial, spawn_frontier_tasks, BackendType, EthApi, FrontierBackend,
- FrontierBlockImport, FrontierPartialComponents, RpcConfig, StorageOverride,
- StorageOverrideHandler,
+use crate::{
+ cli::Sealing,
+ eth::{
+ new_frontier_partial, spawn_frontier_tasks, BackendType, EthApi, FrontierBackend,
+ FrontierBlockImport, FrontierPartialComponents, RpcConfig, StorageOverride,
+ StorageOverrideHandler,
+ },
};
-use futures::future;
-use futures::FutureExt;
-use futures::{channel::mpsc, prelude::*};
+use futures::{channel::mpsc, future, prelude::*, FutureExt};
use futures_timer::Delay;
use sc_client_api::{Backend, BlockBackend};
use sc_consensus::BasicQueue;
-use sc_consensus_babe::BabeLink;
-use sc_consensus_babe::{BabeWorkerHandle, SlotProportion};
+use sc_consensus_babe::{BabeLink, BabeWorkerHandle, SlotProportion};
use sc_consensus_grandpa::SharedVoterState;
#[allow(deprecated)]
pub use sc_executor::NativeElseWasmExecutor;
use sc_service::{error::Error as ServiceError, ChainType, Configuration, TaskManager};
-use sc_telemetry::TelemetryHandle;
-use sc_telemetry::{Telemetry, TelemetryWorker};
+use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker};
use sc_transaction_pool::FullPool;
use sc_transaction_pool_api::OffchainTransactionPoolFactory;
use sp_core::U256;
use sp_runtime::traits::Block as BlockT;
-use std::cell::RefCell;
-use std::{path::Path, sync::Arc, time::Duration};
+use std::{cell::RefCell, path::Path, sync::Arc, time::Duration};
use substrate_prometheus_endpoint::Registry;
use tangle_primitives::Block;
diff --git a/pallets/claims/src/mock.rs b/pallets/claims/src/mock.rs
index 3949bf55..4cedf8ad 100644
--- a/pallets/claims/src/mock.rs
+++ b/pallets/claims/src/mock.rs
@@ -6,9 +6,8 @@ use sp_std::convert::TryFrom;
// The testing primitives are very useful for avoiding having to work with signatures
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required.
use crate::{pallet as pallet_airdrop_claims, sr25519_utils::sub, tests::get_bounded_vec};
-use frame_support::derive_impl;
use frame_support::{
- ord_parameter_types, parameter_types,
+ derive_impl, ord_parameter_types, parameter_types,
traits::{OnFinalize, OnInitialize, WithdrawReasons},
};
use pallet_balances;
diff --git a/pallets/multi-asset-delegation/Cargo.toml b/pallets/multi-asset-delegation/Cargo.toml
index 45be4fb2..a220c7c0 100644
--- a/pallets/multi-asset-delegation/Cargo.toml
+++ b/pallets/multi-asset-delegation/Cargo.toml
@@ -13,13 +13,65 @@ frame-support = { workspace = true }
frame-system = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
+log = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
+ethabi = { workspace = true }
pallet-balances = { workspace = true }
tangle-primitives = { workspace = true }
pallet-assets = { workspace = true, default-features = false }
+fp-evm = { workspace = true }
+itertools = { workspace = true, features = ["use_alloc"] }
+serde = { workspace = true, features = ["derive"], optional = true }
+hex = { workspace = true, features = ["alloc"] }
+
+[dev-dependencies]
+ethereum = { workspace = true, features = ["with-codec"] }
+ethers = "2.0"
+num_enum = { workspace = true }
+hex-literal = { workspace = true }
+libsecp256k1 = { workspace = true }
+pallet-assets = { workspace = true }
+pallet-balances = { workspace = true }
+pallet-timestamp = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+smallvec = { workspace = true }
+sp-io = { workspace = true }
+sp-keystore = { workspace = true }
+
+# Frontier Primitive
+fp-account = { workspace = true }
+fp-consensus = { workspace = true }
+fp-dynamic-fee = { workspace = true }
+fp-ethereum = { workspace = true }
+fp-rpc = { workspace = true }
+fp-self-contained = { workspace = true }
+fp-storage = { workspace = true }
+
+# Frontier FRAME
+pallet-base-fee = { workspace = true }
+pallet-dynamic-fee = { workspace = true }
+pallet-ethereum = { workspace = true }
+pallet-evm = { workspace = true }
+pallet-evm-chain-id = { workspace = true }
+
+pallet-evm-precompile-blake2 = { workspace = true }
+pallet-evm-precompile-bn128 = { workspace = true }
+pallet-evm-precompile-curve25519 = { workspace = true }
+pallet-evm-precompile-ed25519 = { workspace = true }
+pallet-evm-precompile-modexp = { workspace = true }
+pallet-evm-precompile-sha3fips = { workspace = true }
+pallet-evm-precompile-simple = { workspace = true }
+
+precompile-utils = { workspace = true }
+sp-keyring ={ workspace = true}
+pallet-session = { workspace = true }
+pallet-staking = { workspace = true }
+sp-staking = { workspace = true }
+frame-election-provider-support = { workspace = true }
[features]
default = ["std"]
@@ -33,7 +85,33 @@ std = [
"sp-std/std",
"pallet-balances/std",
"pallet-assets/std",
- "tangle-primitives/std"
+ "tangle-primitives/std",
+ "ethabi/std",
+ "log/std",
+ "fp-evm/std",
+ "serde/std",
+ "hex/std",
+
+ "pallet-evm-precompile-modexp/std",
+ "pallet-evm-precompile-sha3fips/std",
+ "pallet-evm-precompile-simple/std",
+ "pallet-evm-precompile-blake2/std",
+ "pallet-evm-precompile-bn128/std",
+ "pallet-evm-precompile-curve25519/std",
+ "pallet-evm-precompile-ed25519/std",
+ "precompile-utils/std",
+ "pallet-staking/std",
+ "fp-account/std",
+ "fp-consensus/std",
+ "fp-dynamic-fee/std",
+ "fp-ethereum/std",
+ "fp-evm/std",
+ "fp-rpc/std",
+ "fp-self-contained/std",
+ "fp-storage/std",
+ "ethabi/std",
+ "sp-keyring/std",
+ "pallet-ethereum/std"
]
try-runtime = ["frame-support/try-runtime"]
runtime-benchmarks = [
diff --git a/pallets/multi-asset-delegation/src/functions.rs b/pallets/multi-asset-delegation/src/functions.rs
index e0a36c9a..9ee9c028 100644
--- a/pallets/multi-asset-delegation/src/functions.rs
+++ b/pallets/multi-asset-delegation/src/functions.rs
@@ -17,6 +17,7 @@ use super::*;
pub mod delegate;
pub mod deposit;
+pub mod evm;
pub mod operator;
pub mod rewards;
pub mod session_manager;
diff --git a/pallets/multi-asset-delegation/src/functions/delegate.rs b/pallets/multi-asset-delegation/src/functions/delegate.rs
index beb35f31..eac6ad6d 100644
--- a/pallets/multi-asset-delegation/src/functions/delegate.rs
+++ b/pallets/multi-asset-delegation/src/functions/delegate.rs
@@ -15,14 +15,18 @@
// along with Tangle. If not, see .
use super::*;
use crate::{types::*, Pallet};
-use frame_support::traits::fungibles::Mutate;
-use frame_support::traits::tokens::Preservation;
-use frame_support::{ensure, pallet_prelude::DispatchResult, traits::Get};
-use sp_runtime::traits::{CheckedSub, Zero};
-use sp_runtime::DispatchError;
-use sp_runtime::Percent;
+use frame_support::{
+ ensure,
+ pallet_prelude::DispatchResult,
+ traits::{fungibles::Mutate, tokens::Preservation, Get},
+};
+use sp_runtime::{
+ traits::{CheckedSub, Zero},
+ DispatchError, Percent,
+};
use sp_std::vec::Vec;
-use tangle_primitives::BlueprintId;
+use tangle_primitives::services::EvmAddressMapping;
+use tangle_primitives::{services::Asset, BlueprintId};
impl Pallet {
/// Processes the delegation of an amount of an asset to an operator.
@@ -42,7 +46,7 @@ impl Pallet {
pub fn process_delegate(
who: T::AccountId,
operator: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
blueprint_selection: DelegatorBlueprintSelection,
) -> DispatchResult {
@@ -141,7 +145,7 @@ impl Pallet {
pub fn process_schedule_delegator_unstake(
who: T::AccountId,
operator: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
Delegators::::try_mutate(&who, |maybe_metadata| {
@@ -272,7 +276,7 @@ impl Pallet {
pub fn process_cancel_delegator_unstake(
who: T::AccountId,
operator: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
Delegators::::try_mutate(&who, |maybe_metadata| {
@@ -388,14 +392,30 @@ impl Pallet {
.checked_sub(&slash_amount)
.ok_or(Error::::InsufficientStakeRemaining)?;
- // Transfer slashed amount to the treasury
- let _ = T::Fungibles::transfer(
- delegation.asset_id,
- &Self::pallet_account(),
- &T::SlashedAmountRecipient::get(),
- slash_amount,
- Preservation::Expendable,
- );
+ match delegation.asset_id {
+ Asset::Custom(asset_id) => {
+ // Transfer slashed amount to the treasury
+ let _ = T::Fungibles::transfer(
+ asset_id,
+ &Self::pallet_account(),
+ &T::SlashedAmountRecipient::get(),
+ slash_amount,
+ Preservation::Expendable,
+ );
+ },
+ Asset::Erc20(address) => {
+ let slashed_amount_recipient_evm =
+ T::EvmAddressMapping::into_address(T::SlashedAmountRecipient::get());
+ let (success, _weight) = Self::erc20_transfer(
+ address,
+ &Self::pallet_evm_account(),
+ slashed_amount_recipient_evm,
+ slash_amount,
+ )
+ .map_err(|_| Error::::ERC20TransferFailed)?;
+ ensure!(success, Error::::ERC20TransferFailed);
+ },
+ }
// emit event
Self::deposit_event(Event::DelegatorSlashed {
diff --git a/pallets/multi-asset-delegation/src/functions/deposit.rs b/pallets/multi-asset-delegation/src/functions/deposit.rs
index 1e5cd4ed..5f7a17fa 100644
--- a/pallets/multi-asset-delegation/src/functions/deposit.rs
+++ b/pallets/multi-asset-delegation/src/functions/deposit.rs
@@ -15,12 +15,14 @@
// along with Tangle. If not, see .
use super::*;
use crate::{types::*, Pallet};
-use frame_support::traits::fungibles::Mutate;
-use frame_support::{ensure, pallet_prelude::DispatchResult};
use frame_support::{
+ ensure,
+ pallet_prelude::DispatchResult,
sp_runtime::traits::{AccountIdConversion, CheckedAdd, Zero},
- traits::{tokens::Preservation, Get},
+ traits::{fungibles::Mutate, tokens::Preservation, Get},
};
+use sp_core::H160;
+use tangle_primitives::services::{Asset, EvmAddressMapping};
impl Pallet {
/// Returns the account ID of the pallet.
@@ -28,6 +30,48 @@ impl Pallet {
T::PalletId::get().into_account_truncating()
}
+ /// Returns the EVM account id of the pallet.
+ ///
+ /// This function retrieves the account id associated with the pallet by converting
+ /// the pallet evm address to an account id.
+ ///
+ /// # Returns
+ /// * `T::AccountId` - The account id of the pallet.
+ pub fn pallet_evm_account() -> H160 {
+ T::EvmAddressMapping::into_address(Self::pallet_account())
+ }
+
+ pub fn handle_transfer_to_pallet(
+ sender: &T::AccountId,
+ asset_id: Asset,
+ amount: BalanceOf,
+ evm_sender: Option,
+ ) -> DispatchResult {
+ match asset_id {
+ Asset::Custom(asset_id) => {
+ T::Fungibles::transfer(
+ asset_id,
+ sender,
+ &Self::pallet_account(),
+ amount,
+ Preservation::Expendable,
+ )?;
+ },
+ Asset::Erc20(asset_address) => {
+ let sender = evm_sender.ok_or(Error::::ERC20TransferFailed)?;
+ let (success, _weight) = Self::erc20_transfer(
+ asset_address,
+ &sender,
+ Self::pallet_evm_account(),
+ amount,
+ )
+ .map_err(|_| Error::::ERC20TransferFailed)?;
+ ensure!(success, Error::::ERC20TransferFailed);
+ },
+ }
+ Ok(())
+ }
+
/// Processes the deposit of assets into the pallet.
///
/// # Arguments
@@ -42,19 +86,14 @@ impl Pallet {
/// the transfer fails.
pub fn process_deposit(
who: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
+ evm_address: Option,
) -> DispatchResult {
ensure!(amount >= T::MinDelegateAmount::get(), Error::::BondTooLow);
// Transfer the amount to the pallet account
- T::Fungibles::transfer(
- asset_id,
- &who,
- &Self::pallet_account(),
- amount,
- Preservation::Expendable,
- )?;
+ Self::handle_transfer_to_pallet(&who, asset_id, amount, evm_address)?;
// Update storage
Delegators::::try_mutate(&who, |maybe_metadata| -> DispatchResult {
@@ -87,7 +126,7 @@ impl Pallet {
/// asset is not supported.
pub fn process_schedule_withdraw(
who: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
Delegators::::try_mutate(&who, |maybe_metadata| {
@@ -126,7 +165,10 @@ impl Pallet {
///
/// Returns an error if the user is not a delegator, if there are no withdraw requests, or if
/// the withdraw request is not ready.
- pub fn process_execute_withdraw(who: T::AccountId) -> DispatchResult {
+ pub fn process_execute_withdraw(
+ who: T::AccountId,
+ evm_address: Option,
+ ) -> DispatchResult {
Delegators::::try_mutate(&who, |maybe_metadata| {
let metadata = maybe_metadata.as_mut().ok_or(Error::::NotDelegator)?;
@@ -137,23 +179,48 @@ impl Pallet {
let delay = T::LeaveDelegatorsDelay::get();
// Process all ready withdraw requests
- metadata.withdraw_requests.retain(|request| {
+ let mut i = 0;
+ while i < metadata.withdraw_requests.len() {
+ let request = &metadata.withdraw_requests[i];
if current_round >= delay + request.requested_round {
- // Transfer the amount back to the delegator
- T::Fungibles::transfer(
- request.asset_id,
- &Self::pallet_account(),
- &who,
- request.amount,
- Preservation::Expendable,
- )
- .expect("Transfer should not fail");
-
- false // Remove this request
+ let transfer_success = match request.asset_id {
+ Asset::Custom(asset_id) => T::Fungibles::transfer(
+ asset_id,
+ &Self::pallet_account(),
+ &who,
+ request.amount,
+ Preservation::Expendable,
+ )
+ .is_ok(),
+ Asset::Erc20(asset_address) => {
+ if let Some(evm_addr) = evm_address {
+ if let Ok((success, _weight)) = Self::erc20_transfer(
+ asset_address,
+ &Self::pallet_evm_account(),
+ evm_addr,
+ request.amount,
+ ) {
+ success
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ },
+ };
+
+ if transfer_success {
+ // Remove the completed request
+ metadata.withdraw_requests.remove(i);
+ } else {
+ // Only increment if we didn't remove the request
+ i += 1;
+ }
} else {
- true // Keep this request
+ i += 1;
}
- });
+ }
Ok(())
})
@@ -172,7 +239,7 @@ impl Pallet {
/// Returns an error if the user is not a delegator or if there is no matching withdraw request.
pub fn process_cancel_withdraw(
who: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
Delegators::::try_mutate(&who, |maybe_metadata| {
diff --git a/pallets/multi-asset-delegation/src/functions/evm.rs b/pallets/multi-asset-delegation/src/functions/evm.rs
new file mode 100644
index 00000000..e2cb6140
--- /dev/null
+++ b/pallets/multi-asset-delegation/src/functions/evm.rs
@@ -0,0 +1,143 @@
+use super::*;
+use crate::types::BalanceOf;
+use ethabi::{Function, StateMutability, Token};
+use frame_support::{
+ dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo},
+ pallet_prelude::{Pays, Weight},
+};
+use parity_scale_codec::Encode;
+use scale_info::prelude::string::String;
+use sp_core::{H160, U256};
+use sp_runtime::traits::UniqueSaturatedInto;
+use sp_std::{vec, vec::Vec};
+use tangle_primitives::services::{EvmGasWeightMapping, EvmRunner};
+
+impl Pallet {
+ /// Moves a `value` amount of tokens from the caller's account to `to`.
+ pub fn erc20_transfer(
+ erc20: H160,
+ from: &H160,
+ to: H160,
+ value: BalanceOf,
+ ) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
+ #[allow(deprecated)]
+ let transfer_fn = Function {
+ name: String::from("transfer"),
+ inputs: vec![
+ ethabi::Param {
+ name: String::from("to"),
+ kind: ethabi::ParamType::Address,
+ internal_type: None,
+ },
+ ethabi::Param {
+ name: String::from("value"),
+ kind: ethabi::ParamType::Uint(256),
+ internal_type: None,
+ },
+ ],
+ outputs: vec![ethabi::Param {
+ name: String::from("success"),
+ kind: ethabi::ParamType::Bool,
+ internal_type: None,
+ }],
+ constant: None,
+ state_mutability: StateMutability::NonPayable,
+ };
+
+ let args = [
+ Token::Address(to),
+ Token::Uint(ethabi::Uint::from(value.using_encoded(U256::from_little_endian))),
+ ];
+
+ log::debug!(target: "evm", "Dispatching EVM call(0x{}): {}", hex::encode(transfer_fn.short_signature()), transfer_fn.signature());
+ let data = transfer_fn.encode_input(&args).map_err(|_| Error::::EVMAbiEncode)?;
+ let gas_limit = 300_000;
+ let info = Self::evm_call(*from, erc20, U256::zero(), data, gas_limit)?;
+ let weight = Self::weight_from_call_info(&info);
+
+ // decode the result and return it
+ let maybe_value = info.exit_reason.is_succeed().then_some(&info.value);
+ let success = if let Some(data) = maybe_value {
+ let result = transfer_fn.decode_output(data).map_err(|_| Error::::EVMAbiDecode)?;
+ let success = result.first().ok_or(Error::::EVMAbiDecode)?;
+ if let ethabi::Token::Bool(val) = success {
+ *val
+ } else {
+ false
+ }
+ } else {
+ false
+ };
+
+ Ok((success, weight))
+ }
+
+ /// Dispatches a call to the EVM and returns the result.
+ fn evm_call(
+ from: H160,
+ to: H160,
+ value: U256,
+ data: Vec,
+ gas_limit: u64,
+ ) -> Result {
+ let transactional = true;
+ let validate = false;
+ let result =
+ T::EvmRunner::call(from, to, data.clone(), value, gas_limit, transactional, validate);
+ match result {
+ Ok(info) => {
+ log::debug!(
+ target: "evm",
+ "Call from: {:?}, to: {:?}, data: 0x{}, gas_limit: {:?}, result: {:?}",
+ from,
+ to,
+ hex::encode(&data),
+ gas_limit,
+ info,
+ );
+ // if we have a revert reason, emit an event
+ if info.exit_reason.is_revert() {
+ log::debug!(
+ target: "evm",
+ "Call to: {:?} with data: 0x{} Reverted with reason: (0x{})",
+ to,
+ hex::encode(&data),
+ hex::encode(&info.value),
+ );
+ #[cfg(test)]
+ eprintln!(
+ "Call to: {:?} with data: 0x{} Reverted with reason: (0x{})",
+ to,
+ hex::encode(&data),
+ hex::encode(&info.value),
+ );
+ Self::deposit_event(Event::::EvmReverted {
+ from,
+ to,
+ data,
+ reason: info.value.clone(),
+ });
+ }
+ Ok(info)
+ },
+ Err(e) => Err(DispatchErrorWithPostInfo {
+ post_info: PostDispatchInfo { actual_weight: Some(e.weight), pays_fee: Pays::Yes },
+ error: e.error.into(),
+ }),
+ }
+ }
+
+ /// Convert the gas used in the call info to weight.
+ pub fn weight_from_call_info(info: &fp_evm::CallInfo) -> Weight {
+ let mut gas_to_weight = T::EvmGasWeightMapping::gas_to_weight(
+ info.used_gas.standard.unique_saturated_into(),
+ true,
+ );
+ if let Some(weight_info) = info.weight_info {
+ if let Some(proof_size_usage) = weight_info.proof_size_usage {
+ *gas_to_weight.proof_size_mut() = proof_size_usage;
+ }
+ }
+ gas_to_weight
+ }
+}
diff --git a/pallets/multi-asset-delegation/src/functions/operator.rs b/pallets/multi-asset-delegation/src/functions/operator.rs
index e35a5fa2..9e6e2b39 100644
--- a/pallets/multi-asset-delegation/src/functions/operator.rs
+++ b/pallets/multi-asset-delegation/src/functions/operator.rs
@@ -17,19 +17,17 @@
/// Functions for the pallet.
use super::*;
use crate::{types::*, Pallet};
-use frame_support::traits::Currency;
-use frame_support::traits::ExistenceRequirement;
-use frame_support::BoundedVec;
use frame_support::{
ensure,
pallet_prelude::DispatchResult,
- traits::{Get, ReservableCurrency},
+ traits::{Currency, ExistenceRequirement, Get, ReservableCurrency},
+ BoundedVec,
};
-use sp_runtime::traits::{CheckedAdd, CheckedSub};
-use sp_runtime::DispatchError;
-use sp_runtime::Percent;
-use tangle_primitives::BlueprintId;
-use tangle_primitives::ServiceManager;
+use sp_runtime::{
+ traits::{CheckedAdd, CheckedSub},
+ DispatchError, Percent,
+};
+use tangle_primitives::{BlueprintId, ServiceManager};
impl Pallet {
/// Handles the deposit of stake amount and creation of an operator.
diff --git a/pallets/multi-asset-delegation/src/functions/rewards.rs b/pallets/multi-asset-delegation/src/functions/rewards.rs
index 860e971e..23288fce 100644
--- a/pallets/multi-asset-delegation/src/functions/rewards.rs
+++ b/pallets/multi-asset-delegation/src/functions/rewards.rs
@@ -14,71 +14,68 @@
// You should have received a copy of the GNU General Public License
// along with Tangle. If not, see .
use super::*;
-use crate::{
- types::{DelegatorBond, *},
- Pallet,
-};
+use crate::{types::*, Pallet};
use frame_support::{ensure, pallet_prelude::DispatchResult, traits::Currency};
-use sp_runtime::{traits::Zero, DispatchError, Saturating};
-use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
-use tangle_primitives::RoundIndex;
+use sp_runtime::DispatchError;
+use sp_std::vec::Vec;
+use tangle_primitives::{services::Asset, RoundIndex};
impl Pallet {
#[allow(clippy::type_complexity)]
- pub fn distribute_rewards(round: RoundIndex) -> DispatchResult {
- let mut delegation_info: BTreeMap<
- T::AssetId,
- Vec, T::AssetId>>,
- > = BTreeMap::new();
-
- // Iterate through all operator snapshots for the given round
- // TODO: Could be dangerous with many operators
- for (_, operator_snapshot) in AtStake::::iter_prefix(round) {
- for delegation in &operator_snapshot.delegations {
- delegation_info.entry(delegation.asset_id).or_default().push(delegation.clone());
- }
- }
-
- // Get the reward configuration
- if let Some(reward_config) = RewardConfigStorage::::get() {
- // Distribute rewards for each asset
- for (asset_id, delegations) in delegation_info.iter() {
- // We only reward asset in a reward vault
- if let Some(vault_id) = AssetLookupRewardVaults::::get(asset_id) {
- if let Some(config) = reward_config.configs.get(&vault_id) {
- // Calculate total amount and distribute rewards
- let total_amount: BalanceOf =
- delegations.iter().fold(Zero::zero(), |acc, d| acc + d.amount);
- let cap: BalanceOf = config.cap;
-
- if total_amount >= cap {
- // Calculate the total reward based on the APY
- let total_reward =
- Self::calculate_total_reward(config.apy, total_amount)?;
-
- for delegation in delegations {
- // Calculate the percentage of the cap that the user is staking
- let staking_percentage =
- delegation.amount.saturating_mul(100u32.into()) / cap;
- // Calculate the reward based on the staking percentage
- let reward =
- total_reward.saturating_mul(staking_percentage) / 100u32.into();
- // Distribute the reward to the delegator
- Self::distribute_reward_to_delegator(
- &delegation.delegator,
- reward,
- )?;
- }
- }
- }
- }
- }
- }
+ pub fn distribute_rewards(_round: RoundIndex) -> DispatchResult {
+ // let mut delegation_info: BTreeMap<
+ // T::AssetId,
+ // Vec, T::AssetId>>,
+ // > = BTreeMap::new();
+
+ // // Iterate through all operator snapshots for the given round
+ // // TODO: Could be dangerous with many operators
+ // for (_, operator_snapshot) in AtStake::::iter_prefix(round) {
+ // for delegation in &operator_snapshot.delegations {
+ // delegation_info.entry(delegation.asset_id).or_default().push(delegation.clone());
+ // }
+ // }
+
+ // // Get the reward configuration
+ // if let Some(reward_config) = RewardConfigStorage::::get() {
+ // // Distribute rewards for each asset
+ // for (asset_id, delegations) in delegation_info.iter() {
+ // // We only reward asset in a reward vault
+ // if let Some(vault_id) = AssetLookupRewardVaults::::get(asset_id) {
+ // if let Some(config) = reward_config.configs.get(&vault_id) {
+ // // Calculate total amount and distribute rewards
+ // let total_amount: BalanceOf =
+ // delegations.iter().fold(Zero::zero(), |acc, d| acc + d.amount);
+ // let cap: BalanceOf = config.cap;
+
+ // if total_amount >= cap {
+ // // Calculate the total reward based on the APY
+ // let total_reward =
+ // Self::calculate_total_reward(config.apy, total_amount)?;
+
+ // for delegation in delegations {
+ // // Calculate the percentage of the cap that the user is staking
+ // let staking_percentage =
+ // delegation.amount.saturating_mul(100u32.into()) / cap;
+ // // Calculate the reward based on the staking percentage
+ // let reward =
+ // total_reward.saturating_mul(staking_percentage) / 100u32.into();
+ // // Distribute the reward to the delegator
+ // Self::distribute_reward_to_delegator(
+ // &delegation.delegator,
+ // reward,
+ // )?;
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
Ok(())
}
- fn calculate_total_reward(
+ fn _calculate_total_reward(
apy: sp_runtime::Percent,
total_amount: BalanceOf,
) -> Result, DispatchError> {
@@ -86,7 +83,7 @@ impl Pallet {
Ok(total_reward)
}
- fn distribute_reward_to_delegator(
+ fn _distribute_reward_to_delegator(
delegator: &T::AccountId,
reward: BalanceOf,
) -> DispatchResult {
@@ -95,7 +92,10 @@ impl Pallet {
Ok(())
}
- pub fn add_asset_to_vault(vault_id: &T::VaultId, asset_id: &T::AssetId) -> DispatchResult {
+ pub fn add_asset_to_vault(
+ vault_id: &T::VaultId,
+ asset_id: &Asset,
+ ) -> DispatchResult {
// Ensure the asset is not already associated with any vault
ensure!(
!AssetLookupRewardVaults::::contains_key(asset_id),
@@ -120,7 +120,10 @@ impl Pallet {
Ok(())
}
- pub fn remove_asset_from_vault(vault_id: &T::VaultId, asset_id: &T::AssetId) -> DispatchResult {
+ pub fn remove_asset_from_vault(
+ vault_id: &T::VaultId,
+ asset_id: &Asset,
+ ) -> DispatchResult {
// Update RewardVaults storage
RewardVaults::::try_mutate(vault_id, |maybe_assets| -> DispatchResult {
let assets = maybe_assets.as_mut().ok_or(Error::::VaultNotFound)?;
diff --git a/pallets/multi-asset-delegation/src/lib.rs b/pallets/multi-asset-delegation/src/lib.rs
index a6f2f0db..6f6d33dc 100644
--- a/pallets/multi-asset-delegation/src/lib.rs
+++ b/pallets/multi-asset-delegation/src/lib.rs
@@ -60,6 +60,9 @@ pub use pallet::*;
#[cfg(test)]
mod mock;
+#[cfg(test)]
+mod mock_evm;
+
#[cfg(test)]
mod tests;
@@ -76,10 +79,7 @@ pub use functions::*;
#[frame_support::pallet]
pub mod pallet {
- use crate::types::*;
-
- use crate::types::{delegator::DelegatorBlueprintSelection, AssetAction};
- use frame_support::traits::fungibles::Inspect;
+ use crate::types::{delegator::DelegatorBlueprintSelection, AssetAction, *};
use frame_support::{
pallet_prelude::*,
traits::{tokens::fungibles, Currency, Get, LockableCurrency, ReservableCurrency},
@@ -87,11 +87,10 @@ pub mod pallet {
};
use frame_system::pallet_prelude::*;
use scale_info::TypeInfo;
+ use sp_core::H160;
use sp_runtime::traits::{MaybeSerializeDeserialize, Member, Zero};
- use sp_std::vec::Vec;
- use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*};
- use tangle_primitives::BlueprintId;
- use tangle_primitives::{traits::ServiceManager, RoundIndex};
+ use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*, vec::Vec};
+ use tangle_primitives::{services::Asset, traits::ServiceManager, BlueprintId, RoundIndex};
/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
@@ -112,6 +111,8 @@ pub mod pallet {
+ Ord
+ Default
+ MaxEncodedLen
+ + Encode
+ + Decode
+ TypeInfo;
/// Type representing the unique ID of a vault.
@@ -188,6 +189,17 @@ pub mod pallet {
/// The address that receives slashed funds
type SlashedAmountRecipient: Get;
+ /// A type that implements the `EvmRunner` trait for the execution of EVM
+ /// transactions.
+ type EvmRunner: tangle_primitives::services::EvmRunner;
+
+ /// A type that implements the `EvmGasWeightMapping` trait for the conversion of EVM gas to
+ /// Substrate weight and vice versa.
+ type EvmGasWeightMapping: tangle_primitives::services::EvmGasWeightMapping;
+
+ /// A type that implements the `EvmAddressMapping` trait for the conversion of EVM address
+ type EvmAddressMapping: tangle_primitives::services::EvmAddressMapping;
+
/// A type representing the weights required by the dispatchables of this pallet.
type WeightInfo: crate::weights::WeightInfo;
}
@@ -231,13 +243,13 @@ pub mod pallet {
#[pallet::getter(fn reward_vaults)]
/// Storage for the reward vaults
pub type RewardVaults =
- StorageMap<_, Blake2_128Concat, T::VaultId, Vec, OptionQuery>;
+ StorageMap<_, Blake2_128Concat, T::VaultId, Vec>, OptionQuery>;
#[pallet::storage]
#[pallet::getter(fn asset_reward_vault_lookup)]
/// Storage for the reward vaults
pub type AssetLookupRewardVaults =
- StorageMap<_, Blake2_128Concat, T::AssetId, T::VaultId, OptionQuery>;
+ StorageMap<_, Blake2_128Concat, Asset, T::VaultId, OptionQuery>;
#[pallet::storage]
#[pallet::getter(fn reward_config)]
@@ -271,9 +283,9 @@ pub mod pallet {
/// An operator has gone online.
OperatorWentOnline { who: T::AccountId },
/// A deposit has been made.
- Deposited { who: T::AccountId, amount: BalanceOf, asset_id: T::AssetId },
+ Deposited { who: T::AccountId, amount: BalanceOf, asset_id: Asset },
/// An withdraw has been scheduled.
- Scheduledwithdraw { who: T::AccountId, amount: BalanceOf, asset_id: T::AssetId },
+ Scheduledwithdraw { who: T::AccountId, amount: BalanceOf, asset_id: Asset },
/// An withdraw has been executed.
Executedwithdraw { who: T::AccountId },
/// An withdraw has been cancelled.
@@ -283,14 +295,14 @@ pub mod pallet {
who: T::AccountId,
operator: T::AccountId,
amount: BalanceOf,
- asset_id: T::AssetId,
+ asset_id: Asset,
},
/// A delegator unstake request has been scheduled.
ScheduledDelegatorBondLess {
who: T::AccountId,
operator: T::AccountId,
amount: BalanceOf,
- asset_id: T::AssetId,
+ asset_id: Asset,
},
/// A delegator unstake request has been executed.
ExecutedDelegatorBondLess { who: T::AccountId },
@@ -304,13 +316,15 @@ pub mod pallet {
AssetUpdatedInVault {
who: T::AccountId,
vault_id: T::VaultId,
- asset_id: T::AssetId,
+ asset_id: Asset,
action: AssetAction,
},
/// Operator has been slashed
OperatorSlashed { who: T::AccountId, amount: BalanceOf },
/// Delegator has been slashed
DelegatorSlashed { who: T::AccountId, amount: BalanceOf },
+ /// EVM execution reverted with a reason.
+ EvmReverted { from: H160, to: H160, data: Vec, reason: Vec },
}
/// Errors emitted by the pallet.
@@ -410,6 +424,12 @@ pub mod pallet {
PendingUnstakeRequestExists,
/// The blueprint is not selected
BlueprintNotSelected,
+ /// Erc20 transfer failed
+ ERC20TransferFailed,
+ /// EVM encode error
+ EVMAbiEncode,
+ /// EVM decode error
+ EVMAbiDecode,
}
/// Hooks for the pallet.
@@ -530,11 +550,12 @@ pub mod pallet {
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
pub fn deposit(
origin: OriginFor,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
+ evm_address: Option,
) -> DispatchResult {
let who = ensure_signed(origin)?;
- Self::process_deposit(who.clone(), asset_id, amount)?;
+ Self::process_deposit(who.clone(), asset_id, amount, evm_address)?;
Self::deposit_event(Event::Deposited { who, amount, asset_id });
Ok(())
}
@@ -544,7 +565,7 @@ pub mod pallet {
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
pub fn schedule_withdraw(
origin: OriginFor,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
let who = ensure_signed(origin)?;
@@ -556,9 +577,9 @@ pub mod pallet {
/// Executes a scheduled withdraw request.
#[pallet::call_index(12)]
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
- pub fn execute_withdraw(origin: OriginFor) -> DispatchResult {
+ pub fn execute_withdraw(origin: OriginFor, evm_address: Option) -> DispatchResult {
let who = ensure_signed(origin)?;
- Self::process_execute_withdraw(who.clone())?;
+ Self::process_execute_withdraw(who.clone(), evm_address)?;
Self::deposit_event(Event::Executedwithdraw { who });
Ok(())
}
@@ -568,7 +589,7 @@ pub mod pallet {
#[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))]
pub fn cancel_withdraw(
origin: OriginFor,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
let who = ensure_signed(origin)?;
@@ -583,7 +604,7 @@ pub mod pallet {
pub fn delegate(
origin: OriginFor,
operator: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
blueprint_selection: DelegatorBlueprintSelection,
) -> DispatchResult {
@@ -605,7 +626,7 @@ pub mod pallet {
pub fn schedule_delegator_unstake(
origin: OriginFor,
operator: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
let who = ensure_signed(origin)?;
@@ -640,7 +661,7 @@ pub mod pallet {
pub fn cancel_delegator_unstake(
origin: OriginFor,
operator: T::AccountId,
- asset_id: T::AssetId,
+ asset_id: Asset,
amount: BalanceOf,
) -> DispatchResult {
let who = ensure_signed(origin)?;
@@ -672,15 +693,6 @@ pub mod pallet {
// Validate cap is not zero
ensure!(!cap.is_zero(), Error::::CapCannotBeZero);
- // Validate the cap is not greater than the total supply
- let asset_ids = RewardVaults::::get(vault_id).ok_or(Error::::VaultNotFound)?;
- for asset_id in asset_ids.iter() {
- ensure!(
- T::Fungibles::total_issuance(*asset_id) >= cap,
- Error::::CapExceedsTotalSupply
- );
- }
-
// Initialize the reward config if not already initialized
RewardConfigStorage::::mutate(|maybe_config| {
let mut config = maybe_config.take().unwrap_or_else(|| RewardConfig {
@@ -735,7 +747,7 @@ pub mod pallet {
pub fn manage_asset_in_vault(
origin: OriginFor,
vault_id: T::VaultId,
- asset_id: T::AssetId,
+ asset_id: Asset,
action: AssetAction,
) -> DispatchResult {
let who = ensure_signed(origin)?;
diff --git a/pallets/multi-asset-delegation/src/mock.rs b/pallets/multi-asset-delegation/src/mock.rs
index 3efa2be6..1314749b 100644
--- a/pallets/multi-asset-delegation/src/mock.rs
+++ b/pallets/multi-asset-delegation/src/mock.rs
@@ -13,75 +13,77 @@
//
// You should have received a copy of the GNU General Public License
// along with Tangle. If not, see .
-use crate as pallet_multi_asset_delegation;
+#![allow(clippy::all)]
+use super::*;
+use crate::{self as pallet_multi_asset_delegation};
+use ethabi::Uint;
+use frame_election_provider_support::{
+ bounds::{ElectionBounds, ElectionBoundsBuilder},
+ onchain, SequentialPhragmen,
+};
use frame_support::{
- derive_impl, parameter_types,
- traits::{AsEnsureOriginWithArg, ConstU16, ConstU32, ConstU64},
+ construct_runtime, derive_impl,
+ pallet_prelude::{Hooks, Weight},
+ parameter_types,
+ traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, OneSessionHandler},
PalletId,
};
+use mock_evm::MockedEvmRunner;
+use pallet_evm::GasWeightMapping;
+use pallet_session::historical as pallet_session_historical;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
-use sp_core::H256;
+use serde_json::json;
+use sp_core::{sr25519, H160};
+use sp_keyring::AccountKeyring;
+use sp_keystore::{testing::MemoryKeystore, KeystoreExt, KeystorePtr};
use sp_runtime::{
- traits::{BlakeTwo256, IdentityLookup},
- BuildStorage,
+ testing::UintAuthorityId,
+ traits::{ConvertInto, IdentityLookup},
+ AccountId32, BuildStorage, Perbill,
};
+use tangle_primitives::services::{EvmAddressMapping, EvmGasWeightMapping, EvmRunner};
-type Block = frame_system::mocking::MockBlock;
-pub type Balance = u64;
-pub type AssetId = u32;
-
-pub const ALICE: u64 = 1;
-pub const BOB: u64 = 2;
-pub const CHARLIE: u64 = 3;
-pub const DAVE: u64 = 4;
-pub const EVE: u64 = 5;
+use core::ops::Mul;
+use std::{collections::BTreeMap, sync::Arc};
-pub const VDOT: AssetId = 1;
+pub type AccountId = AccountId32;
+pub type Balance = u128;
+type Nonce = u32;
+pub type AssetId = u128;
-// Configure a mock runtime to test the pallet.
-frame_support::construct_runtime!(
- pub enum Test
- {
- System: frame_system,
- Balances: pallet_balances,
- Assets: pallet_assets,
- MultiAssetDelegation: pallet_multi_asset_delegation,
- }
-);
-
-#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
-impl frame_system::Config for Test {
+#[frame_support::derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
+ type Nonce = Nonce;
type RuntimeCall = RuntimeCall;
- type Nonce = u64;
- type Hash = H256;
- type Hashing = BlakeTwo256;
- type AccountId = u64;
+ type Hash = sp_core::H256;
+ type Hashing = sp_runtime::traits::BlakeTwo256;
+ type AccountId = AccountId;
type Lookup = IdentityLookup;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
- type BlockHashCount = ConstU64<250>;
+ type BlockHashCount = ();
type Version = ();
type PalletInfo = PalletInfo;
- type AccountData = pallet_balances::AccountData;
+ type AccountData = pallet_balances::AccountData;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
- type SS58Prefix = ConstU16<42>;
+ type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
-impl pallet_balances::Config for Test {
+impl pallet_balances::Config for Runtime {
type Balance = Balance;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
- type ExistentialDeposit = ConstU64<1>;
+ type ExistentialDeposit = ConstU128<1>;
type AccountStore = System;
type MaxLocks = ();
type MaxReserves = ConstU32<50>;
@@ -93,25 +95,193 @@ impl pallet_balances::Config for Test {
type MaxFreezes = ();
}
+parameter_types! {
+ pub ElectionBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default()
+ .voters_count(5_000.into()).targets_count(1_250.into()).build();
+ pub ElectionBoundsMultiPhase: ElectionBounds = ElectionBoundsBuilder::default()
+ .voters_count(10_000.into()).targets_count(1_500.into()).build();
+}
+
+impl pallet_session::historical::Config for Runtime {
+ type FullIdentification = AccountId;
+ type FullIdentificationOf = ConvertInto;
+}
+
+sp_runtime::impl_opaque_keys! {
+ pub struct MockSessionKeys {
+ pub other: MockSessionHandler,
+ }
+}
+
+pub struct MockSessionHandler;
+impl OneSessionHandler for MockSessionHandler {
+ type Key = UintAuthorityId;
+
+ fn on_genesis_session<'a, I: 'a>(_: I)
+ where
+ I: Iterator- ,
+ AccountId: 'a,
+ {
+ }
+
+ fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I)
+ where
+ I: Iterator
- ,
+ AccountId: 'a,
+ {
+ }
+
+ fn on_disabled(_validator_index: u32) {}
+}
+
+impl sp_runtime::BoundToRuntimeAppPublic for MockSessionHandler {
+ type Public = UintAuthorityId;
+}
+
+pub struct MockSessionManager;
+
+impl pallet_session::SessionManager for MockSessionManager {
+ fn end_session(_: sp_staking::SessionIndex) {}
+ fn start_session(_: sp_staking::SessionIndex) {}
+ fn new_session(idx: sp_staking::SessionIndex) -> Option> {
+ if idx == 0 || idx == 1 || idx == 2 {
+ Some(vec![mock_pub_key(1), mock_pub_key(2), mock_pub_key(3), mock_pub_key(4)])
+ } else {
+ None
+ }
+ }
+}
+
+parameter_types! {
+ pub const Period: u64 = 1;
+ pub const Offset: u64 = 0;
+}
+
+impl pallet_session::Config for Runtime {
+ type SessionManager = MockSessionManager;
+ type Keys = MockSessionKeys;
+ type ShouldEndSession = pallet_session::PeriodicSessions;
+ type NextSessionRotation = pallet_session::PeriodicSessions;
+ type SessionHandler = (MockSessionHandler,);
+ type RuntimeEvent = RuntimeEvent;
+ type ValidatorId = AccountId;
+ type ValidatorIdOf = pallet_staking::StashOf;
+ type WeightInfo = ();
+}
+
+pub struct OnChainSeqPhragmen;
+impl onchain::Config for OnChainSeqPhragmen {
+ type System = Runtime;
+ type Solver = SequentialPhragmen;
+ type DataProvider = Staking;
+ type WeightInfo = ();
+ type MaxWinners = ConstU32<100>;
+ type Bounds = ElectionBoundsOnChain;
+}
+
+/// Upper limit on the number of NPOS nominations.
+const MAX_QUOTA_NOMINATIONS: u32 = 16;
+
+impl pallet_staking::Config for Runtime {
+ type Currency = Balances;
+ type CurrencyBalance = ::Balance;
+ type UnixTime = pallet_timestamp::Pallet;
+ type CurrencyToVote = ();
+ type RewardRemainder = ();
+ type RuntimeEvent = RuntimeEvent;
+ type Slash = ();
+ type Reward = ();
+ type SessionsPerEra = ();
+ type SlashDeferDuration = ();
+ type AdminOrigin = frame_system::EnsureRoot;
+ type BondingDuration = ();
+ type SessionInterface = ();
+ type EraPayout = ();
+ type NextNewSession = Session;
+ type MaxExposurePageSize = ConstU32<64>;
+ type MaxControllersInDeprecationBatch = ConstU32<100>;
+ type ElectionProvider = onchain::OnChainExecution;
+ type GenesisElectionProvider = Self::ElectionProvider;
+ type VoterList = pallet_staking::UseNominatorsAndValidatorsMap;
+ type TargetList = pallet_staking::UseValidatorsMap;
+ type MaxUnlockingChunks = ConstU32<32>;
+ type HistoryDepth = ConstU32<84>;
+ type EventListeners = ();
+ type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
+ type NominationsQuota = pallet_staking::FixedNominationsQuota;
+ type WeightInfo = ();
+ type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
+}
+
+parameter_types! {
+ pub const ServicesEVMAddress: H160 = H160([0x11; 20]);
+}
+
+pub struct PalletEVMGasWeightMapping;
+
+impl EvmGasWeightMapping for PalletEVMGasWeightMapping {
+ fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight {
+ pallet_evm::FixedGasWeightMapping::::gas_to_weight(gas, without_base_weight)
+ }
+
+ fn weight_to_gas(weight: Weight) -> u64 {
+ pallet_evm::FixedGasWeightMapping::::weight_to_gas(weight)
+ }
+}
+
+pub struct PalletEVMAddressMapping;
+
+impl EvmAddressMapping for PalletEVMAddressMapping {
+ fn into_account_id(address: H160) -> AccountId {
+ use pallet_evm::AddressMapping;
+ ::AddressMapping::into_account_id(address)
+ }
+
+ fn into_address(account_id: AccountId) -> H160 {
+ H160::from_slice(&AsRef::<[u8; 32]>::as_ref(&account_id)[0..20])
+ }
+}
+
+impl pallet_assets::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type Balance = u128;
+ type AssetId = AssetId;
+ type AssetIdParameter = AssetId;
+ type Currency = Balances;
+ type CreateOrigin = AsEnsureOriginWithArg>;
+ type ForceOrigin = frame_system::EnsureRoot;
+ type AssetDeposit = ConstU128<1>;
+ type AssetAccountDeposit = ConstU128<10>;
+ type MetadataDepositBase = ConstU128<1>;
+ type MetadataDepositPerByte = ConstU128<1>;
+ type ApprovalDeposit = ConstU128<1>;
+ type StringLimit = ConstU32<50>;
+ type Freezer = ();
+ type WeightInfo = ();
+ type CallbackHandle = ();
+ type Extra = ();
+ type RemoveItemsLimit = ConstU32<5>;
+}
+
pub struct MockServiceManager;
-impl tangle_primitives::ServiceManager for MockServiceManager {
- fn get_active_blueprints_count(_account: &u64) -> usize {
+impl tangle_primitives::ServiceManager for MockServiceManager {
+ fn get_active_blueprints_count(_account: &AccountId) -> usize {
// we dont care
Default::default()
}
- fn get_active_services_count(_account: &u64) -> usize {
+ fn get_active_services_count(_account: &AccountId) -> usize {
// we dont care
Default::default()
}
- fn can_exit(_account: &u64) -> bool {
+ fn can_exit(_account: &AccountId) -> bool {
// Mock logic to determine if the given account can exit
true
}
- fn get_blueprints_by_operator(_account: &u64) -> Vec {
+ fn get_blueprints_by_operator(_account: &AccountId) -> Vec {
todo!(); // we dont care
}
}
@@ -122,7 +292,7 @@ parameter_types! {
pub const MinOperatorBondAmount: u64 = 10_000;
pub const BondDuration: u32 = 10;
pub PID: PalletId = PalletId(*b"PotStake");
- pub const SlashedAmountRecipient : u64 = 0;
+ pub SlashedAmountRecipient : AccountId = AccountKeyring::Alice.into();
#[derive(PartialEq, Eq, Clone, Copy, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub const MaxDelegatorBlueprints : u32 = 50;
#[derive(PartialEq, Eq, Clone, Copy, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
@@ -135,7 +305,7 @@ parameter_types! {
pub const MaxDelegations: u32 = 50;
}
-impl pallet_multi_asset_delegation::Config for Test {
+impl pallet_multi_asset_delegation::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type MinOperatorBondAmount = MinOperatorBondAmount;
@@ -145,11 +315,11 @@ impl pallet_multi_asset_delegation::Config for Test {
type OperatorBondLessDelay = ConstU32<1>;
type LeaveDelegatorsDelay = ConstU32<1>;
type DelegationBondLessDelay = ConstU32<5>;
- type MinDelegateAmount = ConstU64<100>;
+ type MinDelegateAmount = ConstU128<100>;
type Fungibles = Assets;
type AssetId = AssetId;
type VaultId = AssetId;
- type ForceOrigin = frame_system::EnsureRoot;
+ type ForceOrigin = frame_system::EnsureRoot;
type PalletId = PID;
type MaxDelegatorBlueprints = MaxDelegatorBlueprints;
type MaxOperatorBlueprints = MaxOperatorBlueprints;
@@ -157,46 +327,291 @@ impl pallet_multi_asset_delegation::Config for Test {
type MaxUnstakeRequests = MaxUnstakeRequests;
type MaxDelegations = MaxDelegations;
type SlashedAmountRecipient = SlashedAmountRecipient;
+ type EvmRunner = MockedEvmRunner;
+ type EvmGasWeightMapping = PalletEVMGasWeightMapping;
+ type EvmAddressMapping = PalletEVMAddressMapping;
type WeightInfo = ();
}
-impl pallet_assets::Config for Test {
- type RuntimeEvent = RuntimeEvent;
- type Balance = u64;
- type AssetId = AssetId;
- type AssetIdParameter = u32;
- type Currency = Balances;
- type CreateOrigin = AsEnsureOriginWithArg>;
- type ForceOrigin = frame_system::EnsureRoot;
- type AssetDeposit = ConstU64<1>;
- type AssetAccountDeposit = ConstU64<10>;
- type MetadataDepositBase = ConstU64<1>;
- type MetadataDepositPerByte = ConstU64<1>;
- type ApprovalDeposit = ConstU64<1>;
- type StringLimit = ConstU32<50>;
- type Freezer = ();
- type WeightInfo = ();
- type CallbackHandle = ();
- type Extra = ();
- type RemoveItemsLimit = ConstU32<5>;
+type Block = frame_system::mocking::MockBlock;
+
+construct_runtime!(
+ pub enum Runtime
+ {
+ System: frame_system,
+ Timestamp: pallet_timestamp,
+ Balances: pallet_balances,
+ Assets: pallet_assets,
+ MultiAssetDelegation: pallet_multi_asset_delegation,
+ EVM: pallet_evm,
+ Ethereum: pallet_ethereum,
+ Session: pallet_session,
+ Staking: pallet_staking,
+ Historical: pallet_session_historical,
+ }
+);
+
+pub struct ExtBuilder;
+
+impl Default for ExtBuilder {
+ fn default() -> Self {
+ ExtBuilder
+ }
+}
+
+pub fn mock_pub_key(id: u8) -> AccountId {
+ sr25519::Public::from_raw([id; 32]).into()
}
+pub fn mock_address(id: u8) -> H160 {
+ H160::from_slice(&[id; 20])
+}
+
+pub fn account_id_to_address(account_id: AccountId) -> H160 {
+ H160::from_slice(&AsRef::<[u8; 32]>::as_ref(&account_id)[0..20])
+}
+
+// pub fn address_to_account_id(address: H160) -> AccountId {
+// use pallet_evm::AddressMapping;
+// ::AddressMapping::into_account_id(address)
+// }
+
pub fn new_test_ext() -> sp_io::TestExternalities {
- let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap();
- pallet_balances::GenesisConfig:: {
- balances: vec![
- (ALICE, 100_000),
- (BOB, 200_000),
- (CHARLIE, 300_000),
- (DAVE, 5_000), // Not enough to stake
- (MultiAssetDelegation::pallet_account(), 100), /* give pallet some ED so it can
- * receive tokens */
- (EVE, 20_000),
- ],
- }
- .assimilate_storage(&mut t)
- .unwrap();
+ new_test_ext_raw_authorities()
+}
+
+pub const USDC_ERC20: H160 = H160([0x23; 20]);
+// pub const USDC: AssetId = 1;
+// pub const WETH: AssetId = 2;
+// pub const WBTC: AssetId = 3;
+pub const VDOT: AssetId = 4;
+
+// This function basically just builds a genesis storage key/value store according to
+// our desired mockup.
+pub fn new_test_ext_raw_authorities() -> sp_io::TestExternalities {
+ let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap();
+ // We use default for brevity, but you can configure as desired if needed.
+ let authorities: Vec = vec![
+ AccountKeyring::Alice.into(),
+ AccountKeyring::Bob.into(),
+ AccountKeyring::Charlie.into(),
+ ];
+ let mut balances: Vec<_> = authorities.iter().map(|i| (i.clone(), 200_000_u128)).collect();
+
+ // Add test accounts with enough balance
+ let test_accounts = vec![
+ AccountKeyring::Dave.into(),
+ AccountKeyring::Eve.into(),
+ MultiAssetDelegation::pallet_account(),
+ ];
+ balances.extend(test_accounts.iter().map(|i: &AccountId| (i.clone(), 1_000_000_u128)));
+
+ pallet_balances::GenesisConfig:: { balances }
+ .assimilate_storage(&mut t)
+ .unwrap();
+
+ let mut evm_accounts = BTreeMap::new();
+
+ for i in 1..=authorities.len() {
+ evm_accounts.insert(
+ mock_address(i as u8),
+ fp_evm::GenesisAccount {
+ code: vec![],
+ storage: Default::default(),
+ nonce: Default::default(),
+ balance: Uint::from(1_000).mul(Uint::from(10).pow(Uint::from(18))),
+ },
+ );
+ }
+
+ for a in &authorities {
+ evm_accounts.insert(
+ account_id_to_address(a.clone()),
+ fp_evm::GenesisAccount {
+ code: vec![],
+ storage: Default::default(),
+ nonce: Default::default(),
+ balance: Uint::from(1_000).mul(Uint::from(10).pow(Uint::from(18))),
+ },
+ );
+ }
+
+ let evm_config =
+ pallet_evm::GenesisConfig:: { accounts: evm_accounts, ..Default::default() };
+
+ evm_config.assimilate_storage(&mut t).unwrap();
+
+ // let assets_config = pallet_assets::GenesisConfig:: {
+ // assets: vec![
+ // (USDC, authorities[0].clone(), true, 100_000), // 1 cent.
+ // (WETH, authorities[1].clone(), true, 100), // 100 wei.
+ // (WBTC, authorities[2].clone(), true, 100), // 100 satoshi.
+ // (VDOT, authorities[0].clone(), true, 100),
+ // ],
+ // metadata: vec![
+ // (USDC, Vec::from(b"USD Coin"), Vec::from(b"USDC"), 6),
+ // (WETH, Vec::from(b"Wrapped Ether"), Vec::from(b"WETH"), 18),
+ // (WBTC, Vec::from(b"Wrapped Bitcoin"), Vec::from(b"WBTC"), 18),
+ // (VDOT, Vec::from(b"VeChain"), Vec::from(b"VDOT"), 18),
+ // ],
+ // accounts: vec![
+ // (USDC, authorities[0].clone(), 1_000_000 * 10u128.pow(6)),
+ // (WETH, authorities[0].clone(), 100 * 10u128.pow(18)),
+ // (WBTC, authorities[0].clone(), 50 * 10u128.pow(18)),
+ // //
+ // (USDC, authorities[1].clone(), 1_000_000 * 10u128.pow(6)),
+ // (WETH, authorities[1].clone(), 100 * 10u128.pow(18)),
+ // (WBTC, authorities[1].clone(), 50 * 10u128.pow(18)),
+ // //
+ // (USDC, authorities[2].clone(), 1_000_000 * 10u128.pow(6)),
+ // (WETH, authorities[2].clone(), 100 * 10u128.pow(18)),
+ // (WBTC, authorities[2].clone(), 50 * 10u128.pow(18)),
+
+ // //
+ // (VDOT, authorities[0].clone(), 1_000_000 * 10u128.pow(6)),
+ // (VDOT, authorities[1].clone(), 1_000_000 * 10u128.pow(6)),
+ // (VDOT, authorities[2].clone(), 1_000_000 * 10u128.pow(6)),
+ // ],
+ // next_asset_id: Some(4),
+ // };
+
+ // assets_config.assimilate_storage(&mut t).unwrap();
let mut ext = sp_io::TestExternalities::new(t);
+ ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr));
ext.execute_with(|| System::set_block_number(1));
+ ext.execute_with(|| {
+ System::set_block_number(1);
+ Session::on_initialize(1);
+ >::on_initialize(1);
+
+ let call = ::EvmRunner::call(
+ MultiAssetDelegation::pallet_evm_account(),
+ USDC_ERC20,
+ serde_json::from_value::(json!({
+ "name": "initialize",
+ "inputs": [
+ {
+ "name": "name_",
+ "type": "string",
+ "internalType": "string"
+ },
+ {
+ "name": "symbol_",
+ "type": "string",
+ "internalType": "string"
+ },
+ {
+ "name": "decimals_",
+ "type": "uint8",
+ "internalType": "uint8"
+ }
+ ],
+ "outputs": [],
+ "stateMutability": "nonpayable"
+ }))
+ .unwrap()
+ .encode_input(&[
+ ethabi::Token::String("USD Coin".to_string()),
+ ethabi::Token::String("USDC".to_string()),
+ ethabi::Token::Uint(6.into()),
+ ])
+ .unwrap(),
+ Default::default(),
+ 300_000,
+ true,
+ false,
+ );
+
+ assert_eq!(call.map(|info| info.exit_reason.is_succeed()).ok(), Some(true));
+ // Mint
+ for i in 1..=authorities.len() {
+ let call = ::EvmRunner::call(
+ MultiAssetDelegation::pallet_evm_account(),
+ USDC_ERC20,
+ serde_json::from_value::(json!({
+ "name": "mint",
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [],
+ "stateMutability": "nonpayable"
+ }))
+ .unwrap()
+ .encode_input(&[
+ ethabi::Token::Address(mock_address(i as u8).into()),
+ ethabi::Token::Uint(Uint::from(100_000).mul(Uint::from(10).pow(Uint::from(6)))),
+ ])
+ .unwrap(),
+ Default::default(),
+ 300_000,
+ true,
+ false,
+ );
+
+ assert_eq!(call.map(|info| info.exit_reason.is_succeed()).ok(), Some(true));
+ }
+ });
+
ext
}
+
+#[macro_export]
+macro_rules! evm_log {
+ () => {
+ fp_evm::Log { address: H160::zero(), topics: vec![], data: vec![] }
+ };
+
+ ($contract:expr) => {
+ fp_evm::Log { address: $contract, topics: vec![], data: vec![] }
+ };
+
+ ($contract:expr, $topic:expr) => {
+ fp_evm::Log {
+ address: $contract,
+ topics: vec![sp_core::keccak_256($topic).into()],
+ data: vec![],
+ }
+ };
+}
+
+// /// Asserts that the EVM logs are as expected.
+// #[track_caller]
+// pub fn assert_evm_logs(expected: &[fp_evm::Log]) {
+// assert_evm_events_contains(expected.iter().cloned().collect())
+// }
+
+// /// Asserts that the EVM events are as expected.
+// #[track_caller]
+// fn assert_evm_events_contains(expected: Vec) {
+// let actual: Vec = System::events()
+// .iter()
+// .filter_map(|e| match e.event {
+// RuntimeEvent::EVM(pallet_evm::Event::Log { ref log }) => Some(log.clone()),
+// _ => None,
+// })
+// .collect();
+
+// // Check if `expected` is a subset of `actual`
+// let mut any_matcher = false;
+// for evt in expected {
+// if !actual.contains(&evt) {
+// panic!("Events don't match\nactual: {actual:?}\nexpected: {evt:?}");
+// } else {
+// any_matcher = true;
+// }
+// }
+
+// // At least one event should be present
+// if !any_matcher {
+// panic!("No events found");
+// }
+// }
diff --git a/pallets/multi-asset-delegation/src/mock_evm.rs b/pallets/multi-asset-delegation/src/mock_evm.rs
new file mode 100644
index 00000000..f9d9646a
--- /dev/null
+++ b/pallets/multi-asset-delegation/src/mock_evm.rs
@@ -0,0 +1,342 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2024 Tangle Foundation.
+//
+// Tangle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Tangle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Tangle. If not, see .
+#![allow(clippy::all)]
+use crate as pallet_multi_asset_delegation;
+use crate::mock::{
+ AccountId, Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, Timestamp,
+};
+use fp_evm::FeeCalculator;
+use frame_support::{
+ parameter_types,
+ traits::{Currency, FindAuthor, OnUnbalanced},
+ weights::Weight,
+ PalletId,
+};
+use pallet_ethereum::{EthereumBlockHashMapping, IntermediateStateRoot, PostLogContent, RawOrigin};
+use pallet_evm::{
+ EnsureAddressNever, EnsureAddressRoot, HashedAddressMapping, OnChargeEVMTransaction,
+};
+use sp_core::{keccak_256, ConstU32, H160, H256, U256};
+use sp_runtime::{
+ traits::{BlakeTwo256, DispatchInfoOf, Dispatchable},
+ transaction_validity::{TransactionValidity, TransactionValidityError},
+ ConsensusEngineId,
+};
+
+use pallet_evm_precompile_blake2::Blake2F;
+use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
+use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_sha3fips::Sha3FIPS256;
+use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
+
+use precompile_utils::precompile_set::{
+ AcceptDelegateCall, AddressU64, CallableByContract, CallableByPrecompile, PrecompileAt,
+ PrecompileSetBuilder, PrecompilesInRangeInclusive,
+};
+
+type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
+
+#[precompile_utils::precompile_name_from_address]
+pub type DefaultPrecompiles = (
+ // Ethereum precompiles:
+ PrecompileAt, ECRecover, EthereumPrecompilesChecks>,
+ PrecompileAt, Sha256, EthereumPrecompilesChecks>,
+ PrecompileAt, Ripemd160, EthereumPrecompilesChecks>,
+ PrecompileAt, Identity, EthereumPrecompilesChecks>,
+ PrecompileAt, Modexp, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Add, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
+ PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
+ PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
+);
+
+pub type TanglePrecompiles = PrecompileSetBuilder<
+ R,
+ (PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<2095>), DefaultPrecompiles>,),
+>;
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = 6000 / 2;
+
+ pub PrecompilesValue: TanglePrecompiles = TanglePrecompiles::<_>::new();
+}
+
+impl pallet_timestamp::Config for Runtime {
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+pub struct FixedGasPrice;
+impl FeeCalculator for FixedGasPrice {
+ fn min_gas_price() -> (U256, Weight) {
+ (1.into(), Weight::zero())
+ }
+}
+
+pub struct FindAuthorTruncated;
+impl FindAuthor for FindAuthorTruncated {
+ fn find_author<'a, I>(_digests: I) -> Option
+ where
+ I: 'a + IntoIterator
- ,
+ {
+ Some(address_build(0).address)
+ }
+}
+
+const BLOCK_GAS_LIMIT: u64 = 150_000_000;
+const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
+
+parameter_types! {
+ pub const TransactionByteFee: u64 = 1;
+ pub const ChainId: u64 = 42;
+ pub const EVMModuleId: PalletId = PalletId(*b"py/evmpa");
+ pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT);
+ pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE);
+ pub const WeightPerGas: Weight = Weight::from_parts(20_000, 0);
+}
+
+parameter_types! {
+ pub SuicideQuickClearLimit: u32 = 0;
+}
+
+pub struct DealWithFees;
+impl OnUnbalanced for DealWithFees {
+ fn on_unbalanceds(_fees_then_tips: impl Iterator
- ) {
+ // whatever
+ }
+}
+pub struct FreeEVMExecution;
+
+impl OnChargeEVMTransaction for FreeEVMExecution {
+ type LiquidityInfo = ();
+
+ fn withdraw_fee(
+ _who: &H160,
+ _fee: U256,
+ ) -> Result> {
+ Ok(())
+ }
+
+ fn correct_and_deposit_fee(
+ _who: &H160,
+ _corrected_fee: U256,
+ _base_fee: U256,
+ already_withdrawn: Self::LiquidityInfo,
+ ) -> Self::LiquidityInfo {
+ already_withdrawn
+ }
+
+ fn pay_priority_fee(_tip: Self::LiquidityInfo) {}
+}
+
+/// Type alias for negative imbalance during fees
+type RuntimeNegativeImbalance =
+ ::AccountId>>::NegativeImbalance;
+
+/// See: [`pallet_evm::EVMCurrencyAdapter`]
+pub struct CustomEVMCurrencyAdapter;
+
+impl OnChargeEVMTransaction for CustomEVMCurrencyAdapter {
+ type LiquidityInfo = Option;
+
+ fn withdraw_fee(
+ who: &H160,
+ fee: U256,
+ ) -> Result> {
+ let pallet_multi_asset_delegation_address =
+ pallet_multi_asset_delegation::Pallet::::pallet_evm_account();
+ // Make pallet multi_asset_delegation account free to use
+ if who == &pallet_multi_asset_delegation_address {
+ return Ok(None);
+ }
+ // fallback to the default implementation
+ as OnChargeEVMTransaction<
+ Runtime,
+ >>::withdraw_fee(who, fee)
+ }
+
+ fn correct_and_deposit_fee(
+ who: &H160,
+ corrected_fee: U256,
+ base_fee: U256,
+ already_withdrawn: Self::LiquidityInfo,
+ ) -> Self::LiquidityInfo {
+ let pallet_multi_asset_delegation_address =
+ pallet_multi_asset_delegation::Pallet::::pallet_evm_account();
+ // Make pallet multi_asset_delegation account free to use
+ if who == &pallet_multi_asset_delegation_address {
+ return already_withdrawn;
+ }
+ // fallback to the default implementation
+ as OnChargeEVMTransaction<
+ Runtime,
+ >>::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn)
+ }
+
+ fn pay_priority_fee(tip: Self::LiquidityInfo) {
+ as OnChargeEVMTransaction<
+ Runtime,
+ >>::pay_priority_fee(tip)
+ }
+}
+
+impl pallet_evm::Config for Runtime {
+ type FeeCalculator = FixedGasPrice;
+ type GasWeightMapping = pallet_evm::FixedGasWeightMapping;
+ type WeightPerGas = WeightPerGas;
+ type BlockHashMapping = EthereumBlockHashMapping;
+ type CallOrigin = EnsureAddressRoot;
+ type WithdrawOrigin = EnsureAddressNever;
+ type AddressMapping = HashedAddressMapping;
+ type Currency = Balances;
+ type RuntimeEvent = RuntimeEvent;
+ type PrecompilesType = TanglePrecompiles;
+ type PrecompilesValue = PrecompilesValue;
+ type ChainId = ChainId;
+ type BlockGasLimit = BlockGasLimit;
+ type Runner = pallet_evm::runner::stack::Runner;
+ type OnChargeTransaction = CustomEVMCurrencyAdapter;
+ type OnCreate = ();
+ type SuicideQuickClearLimit = SuicideQuickClearLimit;
+ type FindAuthor = FindAuthorTruncated;
+ type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
+ type Timestamp = Timestamp;
+ type WeightInfo = ();
+}
+
+parameter_types! {
+ pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes;
+}
+
+impl pallet_ethereum::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type StateRoot = IntermediateStateRoot;
+ type PostLogContent = PostBlockAndTxnHashes;
+ type ExtraDataLength = ConstU32<30>;
+}
+
+impl fp_self_contained::SelfContainedCall for RuntimeCall {
+ type SignedInfo = H160;
+
+ fn is_self_contained(&self) -> bool {
+ match self {
+ RuntimeCall::Ethereum(call) => call.is_self_contained(),
+ _ => false,
+ }
+ }
+
+ fn check_self_contained(&self) -> Option> {
+ match self {
+ RuntimeCall::Ethereum(call) => call.check_self_contained(),
+ _ => None,
+ }
+ }
+
+ fn validate_self_contained(
+ &self,
+ info: &Self::SignedInfo,
+ dispatch_info: &DispatchInfoOf,
+ len: usize,
+ ) -> Option {
+ match self {
+ RuntimeCall::Ethereum(call) => call.validate_self_contained(info, dispatch_info, len),
+ _ => None,
+ }
+ }
+
+ fn pre_dispatch_self_contained(
+ &self,
+ info: &Self::SignedInfo,
+ dispatch_info: &DispatchInfoOf,
+ len: usize,
+ ) -> Option> {
+ match self {
+ RuntimeCall::Ethereum(call) => {
+ call.pre_dispatch_self_contained(info, dispatch_info, len)
+ },
+ _ => None,
+ }
+ }
+
+ fn apply_self_contained(
+ self,
+ info: Self::SignedInfo,
+ ) -> Option>> {
+ match self {
+ call @ RuntimeCall::Ethereum(pallet_ethereum::Call::transact { .. }) => {
+ Some(call.dispatch(RuntimeOrigin::from(RawOrigin::EthereumTransaction(info))))
+ },
+ _ => None,
+ }
+ }
+}
+
+pub struct MockedEvmRunner;
+
+impl tangle_primitives::services::EvmRunner for MockedEvmRunner {
+ type Error = pallet_evm::Error;
+
+ fn call(
+ source: sp_core::H160,
+ target: sp_core::H160,
+ input: Vec,
+ value: sp_core::U256,
+ gas_limit: u64,
+ is_transactional: bool,
+ validate: bool,
+ ) -> Result> {
+ let max_fee_per_gas = FixedGasPrice::min_gas_price().0;
+ let max_priority_fee_per_gas = max_fee_per_gas.saturating_mul(U256::from(2));
+ let nonce = None;
+ let access_list = Default::default();
+ let weight_limit = None;
+ let proof_size_base_cost = None;
+ <::Runner as pallet_evm::Runner>::call(
+ source,
+ target,
+ input,
+ value,
+ gas_limit,
+ Some(max_fee_per_gas),
+ Some(max_priority_fee_per_gas),
+ nonce,
+ access_list,
+ is_transactional,
+ validate,
+ weight_limit,
+ proof_size_base_cost,
+ ::config(),
+ )
+ .map_err(|o| tangle_primitives::services::RunnerError { error: o.error, weight: o.weight })
+ }
+}
+
+pub struct AccountInfo {
+ pub address: H160,
+}
+
+pub fn address_build(seed: u8) -> AccountInfo {
+ let private_key = H256::from_slice(&[(seed + 1); 32]); //H256::from_low_u64_be((i + 1) as u64);
+ let secret_key = libsecp256k1::SecretKey::parse_slice(&private_key[..]).unwrap();
+ let public_key = &libsecp256k1::PublicKey::from_secret_key(&secret_key).serialize()[1..65];
+ let address = H160::from(H256::from(keccak_256(public_key)));
+
+ AccountInfo { address }
+}
diff --git a/pallets/multi-asset-delegation/src/tests/delegate.rs b/pallets/multi-asset-delegation/src/tests/delegate.rs
index 4bde5eb3..92a7316d 100644
--- a/pallets/multi-asset-delegation/src/tests/delegate.rs
+++ b/pallets/multi-asset-delegation/src/tests/delegate.rs
@@ -15,50 +15,58 @@
// along with Tangle. If not, see .
#![allow(clippy::all)]
use super::*;
-use crate::{types::*, CurrentRound, Error};
+use crate::{CurrentRound, Error};
use frame_support::{assert_noop, assert_ok};
-use sp_runtime::Percent;
-use std::collections::BTreeMap;
+use sp_keyring::AccountKeyring::{Alice, Bob};
+use tangle_primitives::services::Asset;
#[test]
fn delegate_should_work() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 100;
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
// Deposit first
- assert_ok!(MultiAssetDelegation::deposit(RuntimeOrigin::signed(who), asset_id, amount,));
+ assert_ok!(MultiAssetDelegation::deposit(
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
+ amount,
+ None
+ ));
assert_ok!(MultiAssetDelegation::delegate(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
Default::default()
));
// Assert
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(metadata.deposits.get(&asset_id).is_none());
assert_eq!(metadata.delegations.len(), 1);
let delegation = &metadata.delegations[0];
- assert_eq!(delegation.operator, operator);
+ assert_eq!(delegation.operator, operator.clone());
assert_eq!(delegation.amount, amount);
assert_eq!(delegation.asset_id, asset_id);
// Check the operator metadata
- let operator_metadata = MultiAssetDelegation::operator_info(operator).unwrap();
+ let operator_metadata = MultiAssetDelegation::operator_info(operator.clone()).unwrap();
assert_eq!(operator_metadata.delegation_count, 1);
assert_eq!(operator_metadata.delegations.len(), 1);
let operator_delegation = &operator_metadata.delegations[0];
- assert_eq!(operator_delegation.delegator, who);
+ assert_eq!(operator_delegation.delegator, who.clone());
assert_eq!(operator_delegation.amount, amount);
assert_eq!(operator_delegation.asset_id, asset_id);
});
@@ -68,42 +76,50 @@ fn delegate_should_work() {
fn schedule_delegator_unstake_should_work() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 100;
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
// Deposit and delegate first
- assert_ok!(MultiAssetDelegation::deposit(RuntimeOrigin::signed(who), asset_id, amount,));
+ assert_ok!(MultiAssetDelegation::deposit(
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
+ amount,
+ None
+ ));
assert_ok!(MultiAssetDelegation::delegate(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
Default::default()
));
assert_ok!(MultiAssetDelegation::schedule_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
));
// Assert
// Check the delegator metadata
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(!metadata.delegator_unstake_requests.is_empty());
let request = &metadata.delegator_unstake_requests[0];
assert_eq!(request.asset_id, asset_id);
assert_eq!(request.amount, amount);
// Check the operator metadata
- let operator_metadata = MultiAssetDelegation::operator_info(operator).unwrap();
+ let operator_metadata = MultiAssetDelegation::operator_info(operator.clone()).unwrap();
assert_eq!(operator_metadata.delegation_count, 0);
assert_eq!(operator_metadata.delegations.len(), 0);
});
@@ -113,38 +129,48 @@ fn schedule_delegator_unstake_should_work() {
fn execute_delegator_unstake_should_work() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 100;
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
// Deposit, delegate and schedule unstake first
- assert_ok!(MultiAssetDelegation::deposit(RuntimeOrigin::signed(who), asset_id, amount,));
+ assert_ok!(MultiAssetDelegation::deposit(
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
+ amount,
+ None
+ ));
assert_ok!(MultiAssetDelegation::delegate(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
Default::default()
));
assert_ok!(MultiAssetDelegation::schedule_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
));
// Simulate round passing
- CurrentRound::::put(10);
+ CurrentRound::::put(10);
- assert_ok!(MultiAssetDelegation::execute_delegator_unstake(RuntimeOrigin::signed(who),));
+ assert_ok!(MultiAssetDelegation::execute_delegator_unstake(RuntimeOrigin::signed(
+ who.clone()
+ ),));
// Assert
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(metadata.delegator_unstake_requests.is_empty());
assert!(metadata.deposits.get(&asset_id).is_some());
assert_eq!(metadata.deposits.get(&asset_id).unwrap(), &amount);
@@ -155,63 +181,71 @@ fn execute_delegator_unstake_should_work() {
fn cancel_delegator_unstake_should_work() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 100;
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
// Deposit, delegate and schedule unstake first
- assert_ok!(MultiAssetDelegation::deposit(RuntimeOrigin::signed(who), asset_id, amount,));
+ assert_ok!(MultiAssetDelegation::deposit(
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
+ amount,
+ None
+ ));
assert_ok!(MultiAssetDelegation::delegate(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
Default::default()
));
assert_ok!(MultiAssetDelegation::schedule_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
));
// Assert
// Check the delegator metadata
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(!metadata.delegator_unstake_requests.is_empty());
let request = &metadata.delegator_unstake_requests[0];
assert_eq!(request.asset_id, asset_id);
assert_eq!(request.amount, amount);
// Check the operator metadata
- let operator_metadata = MultiAssetDelegation::operator_info(operator).unwrap();
+ let operator_metadata = MultiAssetDelegation::operator_info(operator.clone()).unwrap();
assert_eq!(operator_metadata.delegation_count, 0);
assert_eq!(operator_metadata.delegations.len(), 0);
assert_ok!(MultiAssetDelegation::cancel_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount
));
// Assert
// Check the delegator metadata
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(metadata.delegator_unstake_requests.is_empty());
// Check the operator metadata
- let operator_metadata = MultiAssetDelegation::operator_info(operator).unwrap();
+ let operator_metadata = MultiAssetDelegation::operator_info(operator.clone()).unwrap();
assert_eq!(operator_metadata.delegation_count, 1);
assert_eq!(operator_metadata.delegations.len(), 1);
let operator_delegation = &operator_metadata.delegations[0];
- assert_eq!(operator_delegation.delegator, who);
+ assert_eq!(operator_delegation.delegator, who.clone());
assert_eq!(operator_delegation.amount, amount); // Amount added back
assert_eq!(operator_delegation.asset_id, asset_id);
});
@@ -221,67 +255,75 @@ fn cancel_delegator_unstake_should_work() {
fn cancel_delegator_unstake_should_update_already_existing() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 100;
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
// Deposit, delegate and schedule unstake first
- assert_ok!(MultiAssetDelegation::deposit(RuntimeOrigin::signed(who), asset_id, amount,));
+ assert_ok!(MultiAssetDelegation::deposit(
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
+ amount,
+ None
+ ));
assert_ok!(MultiAssetDelegation::delegate(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
Default::default()
));
assert_ok!(MultiAssetDelegation::schedule_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
10,
));
// Assert
// Check the delegator metadata
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(!metadata.delegator_unstake_requests.is_empty());
let request = &metadata.delegator_unstake_requests[0];
assert_eq!(request.asset_id, asset_id);
assert_eq!(request.amount, 10);
// Check the operator metadata
- let operator_metadata = MultiAssetDelegation::operator_info(operator).unwrap();
+ let operator_metadata = MultiAssetDelegation::operator_info(operator.clone()).unwrap();
assert_eq!(operator_metadata.delegation_count, 1);
assert_eq!(operator_metadata.delegations.len(), 1);
let operator_delegation = &operator_metadata.delegations[0];
- assert_eq!(operator_delegation.delegator, who);
+ assert_eq!(operator_delegation.delegator, who.clone());
assert_eq!(operator_delegation.amount, amount - 10);
assert_eq!(operator_delegation.asset_id, asset_id);
assert_ok!(MultiAssetDelegation::cancel_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
10
));
// Assert
// Check the delegator metadata
- let metadata = MultiAssetDelegation::delegators(who).unwrap();
+ let metadata = MultiAssetDelegation::delegators(who.clone()).unwrap();
assert!(metadata.delegator_unstake_requests.is_empty());
// Check the operator metadata
- let operator_metadata = MultiAssetDelegation::operator_info(operator).unwrap();
+ let operator_metadata = MultiAssetDelegation::operator_info(operator.clone()).unwrap();
assert_eq!(operator_metadata.delegation_count, 1);
assert_eq!(operator_metadata.delegations.len(), 1);
let operator_delegation = &operator_metadata.delegations[0];
- assert_eq!(operator_delegation.delegator, who);
+ assert_eq!(operator_delegation.delegator, who.clone());
assert_eq!(operator_delegation.amount, amount); // Amount added back
assert_eq!(operator_delegation.asset_id, asset_id);
});
@@ -291,30 +333,34 @@ fn cancel_delegator_unstake_should_update_already_existing() {
fn delegate_should_fail_if_not_enough_balance() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 10_000;
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
assert_ok!(MultiAssetDelegation::deposit(
- RuntimeOrigin::signed(who),
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
amount - 20,
+ None
));
assert_noop!(
MultiAssetDelegation::delegate(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
Default::default()
),
- Error::::InsufficientBalance
+ Error::::InsufficientBalance
);
});
}
@@ -323,26 +369,34 @@ fn delegate_should_fail_if_not_enough_balance() {
fn schedule_delegator_unstake_should_fail_if_no_delegation() {
new_test_ext().execute_with(|| {
// Arrange
- let who = 1;
- let operator = 2;
- let asset_id = VDOT;
+ let who: AccountId = Bob.into();
+ let operator: AccountId = Alice.into();
+ let asset_id = Asset::Custom(VDOT);
let amount = 100;
- create_and_mint_tokens(VDOT, who, amount);
+ create_and_mint_tokens(VDOT, who.clone(), amount);
- assert_ok!(MultiAssetDelegation::join_operators(RuntimeOrigin::signed(operator), 10_000));
+ assert_ok!(MultiAssetDelegation::join_operators(
+ RuntimeOrigin::signed(operator.clone()),
+ 10_000
+ ));
// Deposit first
- assert_ok!(MultiAssetDelegation::deposit(RuntimeOrigin::signed(who), asset_id, amount,));
+ assert_ok!(MultiAssetDelegation::deposit(
+ RuntimeOrigin::signed(who.clone()),
+ asset_id.clone(),
+ amount,
+ None
+ ));
assert_noop!(
MultiAssetDelegation::schedule_delegator_unstake(
- RuntimeOrigin::signed(who),
- operator,
- asset_id,
+ RuntimeOrigin::signed(who.clone()),
+ operator.clone(),
+ asset_id.clone(),
amount,
),
- Error::::NoActiveDelegation
+ Error::