Skip to content

Commit 9c5be61

Browse files
authored
Allow Treasury to spend Foreign Assets (#3220)
1 parent 47d4d9f commit 9c5be61

File tree

22 files changed

+1124
-91
lines changed

22 files changed

+1124
-91
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/rpc/trace/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ where
244244
self.clone()
245245
.filter(filter)
246246
.await
247-
.map_err(|e| fc_rpc::internal_err(e))
247+
.map_err(fc_rpc::internal_err)
248248
}
249249
}
250250

@@ -709,7 +709,7 @@ where
709709
tracing::trace!("Pooled block {} is no longer requested.", block);
710710
// Remove block from the cache. Drops the value,
711711
// closing all the channels contained in it.
712-
let _ = self.cached_blocks.remove(&block);
712+
let _ = self.cached_blocks.remove(block);
713713
}
714714
}
715715
}

pallets/moonbeam-foreign-assets/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ version = "0.1.0"
88
ethereum-types = { workspace = true }
99
log = { workspace = true }
1010

11+
# Moonbeam
12+
moonbeam-core-primitives = { workspace = true}
13+
1114
# Substrate
1215
frame-support = { workspace = true }
1316
frame-system = { workspace = true }

pallets/moonbeam-foreign-assets/src/evm.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ const ERC20_PAUSE_GAS_LIMIT: u64 = 160_000; // highest failure: 150_500
4242
pub(crate) const ERC20_TRANSFER_GAS_LIMIT: u64 = 160_000; // highest failure: 154_000
4343
pub(crate) const ERC20_APPROVE_GAS_LIMIT: u64 = 160_000; // highest failure: 153_000
4444
const ERC20_UNPAUSE_GAS_LIMIT: u64 = 160_000; // highest failure: 149_500
45+
pub(crate) const ERC20_BALANCE_OF_GAS_LIMIT: u64 = 160_000; // Calculated effective gas: max(used: 24276, pov: 150736, storage: 0) = 150736
4546

4647
#[derive(Debug)]
4748
pub enum EvmError {
4849
BurnFromFail(String),
50+
BalanceOfFail(String),
4951
ContractReturnInvalidValue,
5052
DispatchError(DispatchError),
5153
EvmCallFail(String),
@@ -66,6 +68,10 @@ impl From<EvmError> for XcmError {
6668
log::debug!("BurnFromFail error: {:?}", err);
6769
XcmError::FailedToTransactAsset("Erc20 contract call burnFrom fail")
6870
}
71+
EvmError::BalanceOfFail(err) => {
72+
log::debug!("BalanceOfFail error: {:?}", err);
73+
XcmError::FailedToTransactAsset("Erc20 contract call balanceOf fail")
74+
}
6975
EvmError::ContractReturnInvalidValue => {
7076
XcmError::FailedToTransactAsset("Erc20 contract return invalid value")
7177
}
@@ -449,6 +455,47 @@ impl<T: crate::Config> EvmCaller<T> {
449455

450456
Ok(())
451457
}
458+
459+
// Call contract selector "balanceOf"
460+
pub(crate) fn erc20_balance_of(asset_id: AssetId, account: H160) -> Result<U256, EvmError> {
461+
let mut input = Vec::with_capacity(ERC20_CALL_MAX_CALLDATA_SIZE);
462+
// Selector
463+
input.extend_from_slice(&keccak256!("balanceOf(address)")[..4]);
464+
// append account address
465+
input.extend_from_slice(H256::from(account).as_bytes());
466+
467+
let exec_info = T::EvmRunner::call(
468+
Pallet::<T>::account_id(),
469+
Pallet::<T>::contract_address_from_asset_id(asset_id),
470+
input,
471+
U256::default(),
472+
ERC20_BALANCE_OF_GAS_LIMIT,
473+
None,
474+
None,
475+
None,
476+
Default::default(),
477+
false,
478+
false,
479+
None,
480+
None,
481+
&<T as pallet_evm::Config>::config(),
482+
)
483+
.map_err(|err| EvmError::EvmCallFail(format!("{:?}", err.error.into())))?;
484+
485+
ensure!(
486+
matches!(
487+
exec_info.exit_reason,
488+
ExitReason::Succeed(ExitSucceed::Returned | ExitSucceed::Stopped)
489+
),
490+
{
491+
let err = error_on_execution_failure(&exec_info.exit_reason, &exec_info.value);
492+
EvmError::BalanceOfFail(err)
493+
}
494+
);
495+
496+
let balance = U256::from_big_endian(&exec_info.value);
497+
Ok(balance)
498+
}
452499
}
453500

454501
fn error_on_execution_failure(reason: &ExitReason, data: &[u8]) -> String {

pallets/moonbeam-foreign-assets/src/lib.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use ethereum_types::{H160, U256};
5555
use frame_support::pallet;
5656
use frame_support::pallet_prelude::*;
5757
use frame_support::traits::Contains;
58+
5859
use frame_system::pallet_prelude::*;
5960
use xcm::latest::{
6061
Asset, AssetId as XcmAssetId, Error as XcmError, Fungibility, Location, Result as XcmResult,
@@ -247,6 +248,8 @@ pub mod pallet {
247248
Erc20ContractCreationFail,
248249
EvmCallPauseFail,
249250
EvmCallUnpauseFail,
251+
EvmCallMintIntoFail,
252+
EvmCallTransferFail,
250253
EvmInternalError,
251254
/// Account has insufficient balance for locking
252255
InsufficientBalance,
@@ -364,6 +367,29 @@ pub mod pallet {
364367
.map_err(Into::into)
365368
}
366369

370+
/// Transfer an asset from an account to another one
371+
pub fn transfer(
372+
asset_id: AssetId,
373+
from: T::AccountId,
374+
to: T::AccountId,
375+
amount: U256,
376+
) -> Result<(), evm::EvmError> {
377+
frame_support::storage::with_storage_layer(|| {
378+
EvmCaller::<T>::erc20_transfer(
379+
Self::contract_address_from_asset_id(asset_id),
380+
T::AccountIdToH160::convert(from),
381+
T::AccountIdToH160::convert(to),
382+
amount,
383+
)
384+
})
385+
.map_err(Into::into)
386+
}
387+
388+
pub fn balance(asset_id: AssetId, who: T::AccountId) -> Result<U256, evm::EvmError> {
389+
EvmCaller::<T>::erc20_balance_of(asset_id, T::AccountIdToH160::convert(who))
390+
.map_err(Into::into)
391+
}
392+
367393
/// Aprrove a spender to spend a certain amount of tokens from the owner account
368394
pub fn approve(
369395
asset_id: AssetId,
@@ -754,4 +780,68 @@ pub mod pallet {
754780
AssetsById::<T>::get(asset_id)
755781
}
756782
}
783+
784+
#[cfg(feature = "runtime-benchmarks")]
785+
impl<T: Config> AssetCreate for Pallet<T> {
786+
fn create_asset(id: u128, xcm_location: Location) -> DispatchResult {
787+
use frame_support::traits::OriginTrait;
788+
use sp_std::vec;
789+
Pallet::<T>::create_foreign_asset(
790+
<T as frame_system::Config>::RuntimeOrigin::root(),
791+
id,
792+
xcm_location,
793+
12,
794+
vec![b'M', b'T'].try_into().expect("invalid ticker"),
795+
vec![b'M', b'y', b'T', b'o', b'k']
796+
.try_into()
797+
.expect("invalid name"),
798+
)
799+
}
800+
}
801+
802+
impl<T: Config> AssetMutate<T> for Pallet<T> {
803+
fn transfer_asset(
804+
id: u128,
805+
from: T::AccountId,
806+
to: T::AccountId,
807+
amount: U256,
808+
) -> DispatchResult {
809+
Pallet::<T>::transfer(id, from, to, amount)
810+
.map_err(|_| Error::<T>::EvmCallTransferFail)?;
811+
Ok(())
812+
}
813+
814+
#[cfg(feature = "runtime-benchmarks")]
815+
fn mint_asset(id: u128, account: T::AccountId, amount: U256) -> DispatchResult {
816+
Pallet::<T>::mint_into(id, account, amount)
817+
.map_err(|_| Error::<T>::EvmCallMintIntoFail)?;
818+
Ok(())
819+
}
820+
}
821+
822+
impl<T: Config> AssetInspect for Pallet<T> {
823+
fn asset_exists(id: u128) -> bool {
824+
AssetsById::<T>::contains_key(&id)
825+
}
826+
}
827+
}
828+
829+
pub trait AssetCreate {
830+
fn create_asset(id: u128, xcm_location: Location) -> DispatchResult;
831+
}
832+
833+
pub trait AssetMutate<R: frame_system::Config> {
834+
fn transfer_asset(
835+
id: u128,
836+
from: R::AccountId,
837+
to: R::AccountId,
838+
amount: U256,
839+
) -> DispatchResult;
840+
841+
#[cfg(feature = "runtime-benchmarks")]
842+
fn mint_asset(id: u128, account: R::AccountId, amount: U256) -> DispatchResult;
843+
}
844+
845+
pub trait AssetInspect {
846+
fn asset_exists(id: u128) -> bool;
757847
}

pallets/xcm-weight-trader/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ version = "0.1.0"
88
[dependencies]
99
log = { workspace = true }
1010

11+
# Moonbeam
12+
moonbeam-core-primitives = { workspace = true }
13+
pallet-moonbeam-foreign-assets = { workspace = true}
14+
1115
# Substrate
1216
frame-support = { workspace = true }
1317
frame-system = { workspace = true }
@@ -51,4 +55,4 @@ std = [
5155
"xcm-executor/std",
5256
"xcm-runtime-apis/std",
5357
]
54-
try-runtime = ["frame-support/try-runtime"]
58+
try-runtime = ["frame-support/try-runtime"]

pallets/xcm-weight-trader/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ pub mod pallet {
125125
XcmLocationFiltered,
126126
/// The relative price cannot be zero
127127
PriceCannotBeZero,
128+
/// The relative price calculation overflowed
129+
PriceOverflow,
128130
}
129131

130132
#[pallet::event]
@@ -180,7 +182,7 @@ pub mod pallet {
180182
}
181183

182184
#[pallet::call_index(1)]
183-
#[pallet::weight(T::WeightInfo::edit_asset())]
185+
#[pallet::weight(<T as pallet::Config>::WeightInfo::edit_asset())]
184186
pub fn edit_asset(
185187
origin: OriginFor<T>,
186188
location: Location,
@@ -205,7 +207,7 @@ pub mod pallet {
205207
}
206208

207209
#[pallet::call_index(2)]
208-
#[pallet::weight(T::WeightInfo::pause_asset_support())]
210+
#[pallet::weight(<T as pallet::Config>::WeightInfo::pause_asset_support())]
209211
pub fn pause_asset_support(origin: OriginFor<T>, location: Location) -> DispatchResult {
210212
T::PauseSupportedAssetOrigin::ensure_origin(origin)?;
211213

@@ -221,7 +223,7 @@ pub mod pallet {
221223
}
222224

223225
#[pallet::call_index(3)]
224-
#[pallet::weight(T::WeightInfo::resume_asset_support())]
226+
#[pallet::weight(<T as pallet::Config>::WeightInfo::resume_asset_support())]
225227
pub fn resume_asset_support(origin: OriginFor<T>, location: Location) -> DispatchResult {
226228
T::ResumeSupportedAssetOrigin::ensure_origin(origin)?;
227229

@@ -237,7 +239,7 @@ pub mod pallet {
237239
}
238240

239241
#[pallet::call_index(4)]
240-
#[pallet::weight(T::WeightInfo::remove_asset())]
242+
#[pallet::weight(<T as pallet::Config>::WeightInfo::remove_asset())]
241243
pub fn remove_asset(origin: OriginFor<T>, location: Location) -> DispatchResult {
242244
T::RemoveSupportedAssetOrigin::ensure_origin(origin)?;
243245

primitives/xcm/src/ethereum_xcm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl XcmToEthereum for EthereumXcmTransactionV1 {
203203
s: rs_id(),
204204
}))
205205
}
206-
_ => return None,
206+
_ => None,
207207
}
208208
}
209209
}
@@ -222,7 +222,7 @@ impl XcmToEthereum for EthereumXcmTransactionV2 {
222222
let from_tuple_to_access_list = |t: &Vec<(H160, Vec<H256>)>| -> AccessList {
223223
t.iter()
224224
.map(|item| AccessListItem {
225-
address: item.0.clone(),
225+
address: item.0,
226226
storage_keys: item.1.clone(),
227227
})
228228
.collect::<Vec<AccessListItem>>()

runtime/common/src/benchmarking.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,22 @@
1515
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
1616

1717
use account::AccountId20;
18+
use frame_support::traits::fungible::NativeOrWithId;
19+
use moonbeam_core_primitives::AssetId;
1820
use pallet_treasury::ArgumentsFactory;
1921

2022
pub struct BenchmarkHelper;
2123

22-
impl ArgumentsFactory<(), AccountId20> for BenchmarkHelper {
23-
fn create_asset_kind(_seed: u32) -> () {
24-
()
24+
impl ArgumentsFactory<NativeOrWithId<AssetId>, AccountId20> for BenchmarkHelper {
25+
fn create_asset_kind(seed: u32) -> NativeOrWithId<AssetId> {
26+
NativeOrWithId::WithId(seed.into())
2527
}
2628

2729
fn create_beneficiary(seed: [u8; 32]) -> AccountId20 {
30+
// Avoid generating a zero address
31+
if seed == [0; 32] {
32+
return AccountId20::from([1; 32]);
33+
}
2834
AccountId20::from(seed)
2935
}
3036
}

0 commit comments

Comments
 (0)