Skip to content

Commit

Permalink
Foreign Assets Migration (#3020)
Browse files Browse the repository at this point in the history
* add a call to freeze asset and create smart contract

* add a storage value to track foreign asset migration status

* add a call to migrate balances

* migrate approvals

* update migration calls

* add test for start_foreign_asset_migration call

* add test for balances migrations

* add tests for approvals migration

* add test for finish migration

* add typescript tests

* update polkadot-sdk pin

* add benchmarking scenarios

* update typescript tests

* update benchmark scenarios

* fix revet when minting and approving

* add local genretad weights

* fix format

* format

* fix test build

* fix format

* fix test

* address comments and add more tests

* Update pallets/moonbeam-lazy-migrations/src/lib.rs

Co-authored-by: Rodrigo Quelhas <22591718+RomarQ@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Éloïs <c@elo.tf>

* allow multiple assets to be migrated

* update call to approve migration of assets

* update benchmarks

* fix rust tests

* fix typescript tests

* update origin

* fix ci

* fix benchmarks

---------

Co-authored-by: Rodrigo Quelhas <22591718+RomarQ@users.noreply.github.com>
Co-authored-by: Éloïs <c@elo.tf>
  • Loading branch information
3 people authored Dec 4, 2024
1 parent 99eebab commit 317f49c
Show file tree
Hide file tree
Showing 19 changed files with 3,257 additions and 342 deletions.
565 changes: 286 additions & 279 deletions Cargo.lock

Large diffs are not rendered by default.

50 changes: 48 additions & 2 deletions pallets/moonbeam-foreign-assets/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ use xcm::latest::Error as XcmError;
const ERC20_CALL_MAX_CALLDATA_SIZE: usize = 4 + 32 + 32; // selector + address + uint256
const ERC20_CREATE_MAX_CALLDATA_SIZE: usize = 16 * 1024; // 16Ko

// Hardcoded gas limits (from manueal binary search)
const ERC20_CREATE_GAS_LIMIT: u64 = 3_367_000; // highest failure: 3_366_000
// Hardcoded gas limits (from manual binary search)
const ERC20_CREATE_GAS_LIMIT: u64 = 3_410_000; // highest failure: 3_406_000
pub(crate) const ERC20_BURN_FROM_GAS_LIMIT: u64 = 155_000; // highest failure: 154_000
pub(crate) const ERC20_MINT_INTO_GAS_LIMIT: u64 = 155_000; // highest failure: 154_000
const ERC20_PAUSE_GAS_LIMIT: u64 = 150_000; // highest failure: 149_500
pub(crate) const ERC20_TRANSFER_GAS_LIMIT: u64 = 155_000; // highest failure: 154_000
pub(crate) const ERC20_APPROVE_GAS_LIMIT: u64 = 154_000; // highest failure: 153_000
const ERC20_UNPAUSE_GAS_LIMIT: u64 = 150_000; // highest failure: 149_500

pub enum EvmError {
Expand Down Expand Up @@ -247,6 +248,51 @@ impl<T: crate::Config> EvmCaller<T> {
Ok(())
}

pub(crate) fn erc20_approve(
erc20_contract_address: H160,
owner: H160,
spender: H160,
amount: U256,
) -> Result<(), EvmError> {
let mut input = Vec::with_capacity(ERC20_CALL_MAX_CALLDATA_SIZE);
// Selector
input.extend_from_slice(&keccak256!("approve(address,uint256)")[..4]);
// append spender address
input.extend_from_slice(H256::from(spender).as_bytes());
// append amount to be approved
input.extend_from_slice(H256::from_uint(&amount).as_bytes());
let weight_limit: Weight =
T::GasWeightMapping::gas_to_weight(ERC20_APPROVE_GAS_LIMIT, true);

let exec_info = T::EvmRunner::call(
owner,
erc20_contract_address,
input,
U256::default(),
ERC20_APPROVE_GAS_LIMIT,
None,
None,
None,
Default::default(),
false,
false,
Some(weight_limit),
Some(0),
&<T as pallet_evm::Config>::config(),
)
.map_err(|_| EvmError::EvmCallFail)?;

ensure!(
matches!(
exec_info.exit_reason,
ExitReason::Succeed(ExitSucceed::Returned | ExitSucceed::Stopped)
),
EvmError::EvmCallFail
);

Ok(())
}

pub(crate) fn erc20_burn_from(
erc20_contract_address: H160,
who: H160,
Expand Down
69 changes: 68 additions & 1 deletion pallets/moonbeam-foreign-assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,61 @@ pub mod pallet {

/// Compute asset contract address from asset id
#[inline]
pub(crate) fn contract_address_from_asset_id(asset_id: AssetId) -> H160 {
pub fn contract_address_from_asset_id(asset_id: AssetId) -> H160 {
let mut buffer = [0u8; 20];
buffer[..4].copy_from_slice(&FOREIGN_ASSETS_PREFIX);
buffer[4..].copy_from_slice(&asset_id.to_be_bytes());
H160(buffer)
}

pub fn register_foreign_asset(
asset_id: AssetId,
xcm_location: Location,
decimals: u8,
symbol: BoundedVec<u8, ConstU32<256>>,
name: BoundedVec<u8, ConstU32<256>>,
) -> DispatchResult {
// Ensure such an assetId does not exist
ensure!(
!AssetsById::<T>::contains_key(&asset_id),
Error::<T>::AssetAlreadyExists
);

ensure!(
!AssetsByLocation::<T>::contains_key(&xcm_location),
Error::<T>::LocationAlreadyExists
);

ensure!(
AssetsById::<T>::count() < T::MaxForeignAssets::get(),
Error::<T>::TooManyForeignAssets
);

ensure!(
T::AssetIdFilter::contains(&asset_id),
Error::<T>::AssetIdFiltered
);

let symbol = core::str::from_utf8(&symbol).map_err(|_| Error::<T>::InvalidSymbol)?;
let name = core::str::from_utf8(&name).map_err(|_| Error::<T>::InvalidTokenName)?;

let contract_address = EvmCaller::<T>::erc20_create(asset_id, decimals, symbol, name)?;

// Insert the association assetId->foreigAsset
// Insert the association foreigAsset->assetId
AssetsById::<T>::insert(&asset_id, &xcm_location);
AssetsByLocation::<T>::insert(&xcm_location, (asset_id, AssetStatus::Active));

T::OnForeignAssetCreated::on_asset_created(&xcm_location, &asset_id);

Self::deposit_event(Event::ForeignAssetCreated {
contract_address,
asset_id,
xcm_location,
});
Ok(())
}

/// Mint an asset into a specific account
pub fn mint_into(
asset_id: AssetId,
Expand All @@ -259,6 +307,25 @@ pub mod pallet {
})
.map_err(Into::into)
}

/// Aprrove a spender to spend a certain amount of tokens from the owner account
pub fn approve(
asset_id: AssetId,
owner: T::AccountId,
spender: T::AccountId,
amount: U256,
) -> Result<(), evm::EvmError> {
// We perform the evm call in a storage transaction to ensure that if it fail
// any contract storage changes are rolled back.
EvmCaller::<T>::erc20_approve(
Self::contract_address_from_asset_id(asset_id),
T::AccountIdToH160::convert(owner),
T::AccountIdToH160::convert(spender),
amount,
)
.map_err(Into::into)
}

pub fn weight_of_erc20_burn() -> Weight {
T::GasWeightMapping::gas_to_weight(evm::ERC20_BURN_FROM_GAS_LIMIT, true)
}
Expand Down
12 changes: 12 additions & 0 deletions pallets/moonbeam-lazy-migrations/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-assets = { workspace = true }
pallet-asset-manager = { workspace = true }
pallet-balances = { workspace = true }
pallet-moonbeam-foreign-assets = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true, features = ["derive"] }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
xcm = { workspace = true }
xcm-primitives = { workspace = true }

environmental = { workspace = true }

# Frontier
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
Expand All @@ -34,13 +40,15 @@ frame-benchmarking = { workspace = true, optional = true }
frame-benchmarking = { workspace = true, features = ["std"] }
pallet-balances = { workspace = true, features = ["std", "insecure_zero_ed"] }
pallet-timestamp = { workspace = true, features = ["std"] }
precompile-utils = { workspace = true, features = ["std"] }
rlp = { workspace = true, features = ["std"] }
sp-io = { workspace = true, features = ["std"] }

[features]
default = ["std"]
runtime-benchmarks = ["frame-benchmarking"]
std = [
"environmental/std",
"pallet-balances/std",
"frame-support/std",
"frame-system/std",
Expand All @@ -52,7 +60,11 @@ std = [
"pallet-evm/std",
"pallet-timestamp/std",
"pallet-assets/std",
"pallet-moonbeam-foreign-assets/std",
"pallet-asset-manager/std",
"precompile-utils/std",
"cumulus-primitives-storage-weight-reclaim/std",
"rlp/std",
"xcm-primitives/std",
]
try-runtime = ["frame-support/try-runtime"]
Loading

0 comments on commit 317f49c

Please sign in to comment.