Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Polkadot assets on ethereum #128

Merged
merged 77 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
6019c8d
Register token from polkadot
yrong Mar 21, 2024
c613fc6
Extract AssetRegistrarMetadata
yrong Mar 21, 2024
9672b52
ReserveTransfer from AssetHub
yrong Mar 22, 2024
c017bfe
Transfer DOT back to AssetHub
yrong Mar 22, 2024
3dc37ea
Fix breaking tests
yrong Mar 22, 2024
1f00024
Fix register token
yrong Mar 25, 2024
a20a8b7
Increase dispatch_gas to cover the actual cost for register token
yrong Mar 26, 2024
41104d5
Add ConvertAssetId to outbound router
yrong Mar 26, 2024
a8b006b
Rename to SendForeignToken
yrong Mar 27, 2024
b8a75d1
Update cost
yrong Mar 29, 2024
fec5a63
Move Command.RegisterToken to top level
yrong Apr 4, 2024
a683680
Use VersionedLocation for storage
yrong Apr 4, 2024
3621de4
Use versioned location
yrong Apr 4, 2024
f7ed096
TokenIdOf follow the same pattern as AgentIdOf
yrong Apr 5, 2024
36bb656
Rename MintToken to TransferNativeToken
yrong Apr 5, 2024
cf091f6
Rename as SendNativeToken
yrong Apr 5, 2024
4921280
Merge branch 'snowbridge' into ron/polkadot-assets-on-ethereum
yrong Apr 5, 2024
10c5e10
More refactoring
yrong Apr 5, 2024
587fb6e
Remove AgentExecuteCommand
yrong Apr 9, 2024
f56567d
Remove TokenExists check
yrong Apr 10, 2024
c792204
Update cumulus/parachains/integration-tests/emulated/tests/bridges/br…
yrong Apr 10, 2024
a81b87f
Merge branch 'ron/polkadot-assets-on-ethereum' of https://github.com/…
yrong Apr 10, 2024
ce91a8c
Benchmark register_token
yrong Apr 10, 2024
f3c9d11
More checks for the events
yrong Apr 10, 2024
41aa563
More tests for send native token
yrong Apr 10, 2024
d6c6330
More tests describe tokenID
yrong Apr 10, 2024
faf8f73
More asset identifiers
yrong Apr 10, 2024
7001866
Add LocationToToken & Always use VersionedLocation
yrong Apr 10, 2024
1edad16
Fix breaking tests
yrong Apr 10, 2024
933130a
Update gas cost for registering polkadot token
yrong Apr 10, 2024
8ace79e
Merge branch 'bridge-next-gen' into ron/polkadot-assets-on-ethereum
yrong Apr 11, 2024
40cc470
Add AgentExecuteCommand back for compatibility
yrong Apr 11, 2024
2d8f3b1
Merge branch 'bridge-next-gen' into ron/polkadot-assets-on-ethereum
yrong Apr 16, 2024
5a4f3af
Split into 2 tests each covers one direction
yrong Apr 16, 2024
50983ed
Revert the change on AH
yrong Apr 19, 2024
5ad44df
Switch to native token on penpal for the integration
yrong Apr 19, 2024
8a79d89
Make fee asset as configuration parameter of the Channel
yrong Apr 26, 2024
0251493
Revert fee_asset_id as channel property
yrong May 7, 2024
fa70f7b
Short epoch
yrong Aug 21, 2024
3d0fae3
Merge branch 'snowbridge' into ron/polkadot-assets-on-ethereum
yrong Aug 22, 2024
25c819a
Fix integration tests
yrong Aug 22, 2024
3cb2f05
Fix format
yrong Aug 22, 2024
3a94733
Cleanup
yrong Aug 22, 2024
9d900b2
Register relay token
yrong Aug 22, 2024
33ea85e
Use relay token as fee asset
yrong Aug 22, 2024
d64e783
Fix clippy
yrong Aug 22, 2024
79fb3b5
Fix missing dependency
yrong Aug 23, 2024
8609eed
Remove reanchored prefix from asset_id
yrong Aug 26, 2024
3f1ded3
Fix test
yrong Aug 26, 2024
0e06cae
Fix the instruction
yrong Aug 26, 2024
4a04f1f
Fix test
yrong Aug 26, 2024
0d3988c
Multi hop transfer
yrong Aug 27, 2024
f1155b1
Fix tests
yrong Aug 27, 2024
a44db43
allow bridge hub assets
alistair-singh Aug 28, 2024
0f0fd8e
Register token from BH directly and remove force_register_token
yrong Aug 28, 2024
f5c6b92
Decrease gas estimation for PNA
yrong Aug 28, 2024
1095bff
Cleanup
yrong Aug 28, 2024
21fa444
Merge branch 'ron/short-epoch' into ron/polkadot-assets-on-ethereum
yrong Aug 28, 2024
2675241
Store Location rather than VersionedLocation
yrong Aug 29, 2024
42933da
Fix test
yrong Aug 29, 2024
760d697
Merge branch 'snowbridge' into ron/polkadot-assets-on-ethereum
yrong Aug 29, 2024
a550c42
Add more tests
yrong Aug 29, 2024
736fe90
Revamp reanchor logic
yrong Aug 29, 2024
d1b8d16
Improve reanchor & Fix tests
yrong Aug 29, 2024
897fdad
Use secondary governance channel to register PNA
yrong Aug 29, 2024
e733321
Rename as asset location
yrong Aug 29, 2024
31c75e9
Use BoundVec limit size of name&symbol
yrong Aug 29, 2024
009402e
Describe location of PNA & more tests
yrong Aug 29, 2024
949fb45
Add test
yrong Aug 30, 2024
b38b20c
Fix taplo
yrong Aug 30, 2024
0b2f782
More tests
yrong Aug 30, 2024
62eef65
Cleanup
yrong Aug 30, 2024
39a84b9
Format code
yrong Aug 30, 2024
5ae8b0b
Batch rename token command
yrong Aug 30, 2024
6ca963d
Remove agent_id
yrong Aug 30, 2024
79e62fa
Rename as AssetMetadata
yrong Aug 30, 2024
8a4f582
Add test for penpal native token
yrong Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions bridges/snowbridge/pallets/inbound-queue/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ use snowbridge_beacon_primitives::{
use snowbridge_core::{
gwei,
inbound::{Log, Proof, VerificationError},
meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup,
meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId,
};
use snowbridge_router_primitives::inbound::MessageToXcm;
use sp_core::{H160, H256};
use sp_runtime::{
traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify},
traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify},
BuildStorage, FixedU128, MultiSignature,
};
use sp_std::{convert::From, default::Default};
Expand Down Expand Up @@ -242,6 +242,16 @@ impl TransactAsset for SuccessfulTransactor {
}
}

pub struct MockTokenIdConvert;
impl MaybeEquivalence<TokenId, Location> for MockTokenIdConvert {
fn convert(_id: &TokenId) -> Option<Location> {
Some(Location { parents: 1, interior: GlobalConsensus(Rococo).into() })
}
fn convert_back(_loc: &Location) -> Option<TokenId> {
None
}
}

impl inbound_queue::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Verifier = MockVerifier;
Expand All @@ -255,6 +265,7 @@ impl inbound_queue::Config for Test {
InboundQueuePalletInstance,
AccountId,
Balance,
MockTokenIdConvert,
>;
type PricingParameters = Parameters;
type ChannelLookup = MockChannelLookup;
Expand Down
119 changes: 114 additions & 5 deletions bridges/snowbridge/pallets/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,16 @@ use frame_system::pallet_prelude::*;
use snowbridge_core::{
meth,
outbound::{Command, Initializer, Message, OperatingMode, SendError, SendMessage},
sibling_sovereign_account, AgentId, Channel, ChannelId, ParaId,
PricingParameters as PricingParametersRecord, PRIMARY_GOVERNANCE_CHANNEL,
sibling_sovereign_account, token_id_of, AgentId, AssetRegistrarMetadata, Channel, ChannelId,
ParaId, PricingParameters as PricingParametersRecord, TokenId, PRIMARY_GOVERNANCE_CHANNEL,
SECONDARY_GOVERNANCE_CHANNEL,
};
use sp_core::{RuntimeDebug, H160, H256};
use sp_io::hashing::blake2_256;
use sp_runtime::{traits::BadOrigin, DispatchError, SaturatedConversion};
use sp_runtime::{
traits::{BadOrigin, MaybeEquivalence},
DispatchError, SaturatedConversion,
};
use sp_std::prelude::*;
use xcm::prelude::*;
use xcm_executor::traits::ConvertLocation;
Expand Down Expand Up @@ -99,7 +102,7 @@ where
}

/// Hash the location to produce an agent id
fn agent_id_of<T: Config>(location: &Location) -> Result<H256, DispatchError> {
pub fn agent_id_of<T: Config>(location: &Location) -> Result<H256, DispatchError> {
T::AgentIdOf::convert_location(location).ok_or(Error::<T>::LocationConversionFailed.into())
}

Expand Down Expand Up @@ -127,7 +130,7 @@ where

#[frame_support::pallet]
pub mod pallet {
use snowbridge_core::StaticLookup;
use snowbridge_core::{outbound::AgentExecuteCommand, StaticLookup};
use sp_core::U256;

use super::*;
Expand Down Expand Up @@ -211,6 +214,12 @@ pub mod pallet {
PricingParametersChanged {
params: PricingParametersOf<T>,
},
/// Register token
RegisterToken {
asset_id: Location,
yrong marked this conversation as resolved.
Show resolved Hide resolved
token_id: H256,
agent_id: AgentId,
yrong marked this conversation as resolved.
Show resolved Hide resolved
},
}

#[pallet::error]
Expand All @@ -226,6 +235,7 @@ pub mod pallet {
InvalidTokenTransferFees,
InvalidPricingParameters,
InvalidUpgradeParameters,
TokenExists,
}

/// The set of registered agents
Expand All @@ -243,6 +253,10 @@ pub mod pallet {
pub type PricingParameters<T: Config> =
StorageValue<_, PricingParametersOf<T>, ValueQuery, T::DefaultPricingParameters>;

#[pallet::storage]
#[pallet::getter(fn tokens)]
pub type Tokens<T: Config> = StorageMap<_, Twox64Concat, TokenId, Location, OptionQuery>;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto. Location version is implicitly v4.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was rolled back.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alistair-singh We should not use VersionedLocation for fields in storage. There is other commentary in the PR about this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is stopping someone from rolling over xcm versions prelude without us knowing and breaking the lookup of Tokens?

Copy link
Author

@yrong yrong Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With paritytech@5ef32c8 will just not compile.


#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
Expand Down Expand Up @@ -574,6 +588,57 @@ pub mod pallet {
});
Ok(())
}

/// Sends a message to the Gateway contract to register a new
/// token that represents `asset`.
///
/// - `origin`: Must be `MultiLocation` from sibling parachain
#[pallet::call_index(10)]
#[pallet::weight(T::WeightInfo::register_token())]
pub fn register_token(
origin: OriginFor<T>,
asset: Box<VersionedLocation>,
yrong marked this conversation as resolved.
Show resolved Hide resolved
metadata: AssetRegistrarMetadata,
yrong marked this conversation as resolved.
Show resolved Hide resolved
) -> DispatchResult {
let origin_location: Location = T::SiblingOrigin::ensure_origin(origin)?;
vgeddes marked this conversation as resolved.
Show resolved Hide resolved

let (para_id, agent_id) = ensure_sibling::<T>(&origin_location)?;

let asset: Location =
(*asset).try_into().map_err(|_| Error::<T>::UnsupportedLocationVersion)?;

Self::do_register_token(para_id, agent_id, asset, metadata)?;

Ok(())
}

/// Sends a message to the Gateway contract to register a new
/// token that represents `asset`.
///
/// - `origin`: Must be `MultiLocation`
#[pallet::call_index(11)]
#[pallet::weight(T::WeightInfo::register_token())]
claravanstaden marked this conversation as resolved.
Show resolved Hide resolved
pub fn force_register_token(
vgeddes marked this conversation as resolved.
Show resolved Hide resolved
vgeddes marked this conversation as resolved.
Show resolved Hide resolved
yrong marked this conversation as resolved.
Show resolved Hide resolved
origin: OriginFor<T>,
location: Box<VersionedLocation>,
asset: Box<VersionedLocation>,
metadata: AssetRegistrarMetadata,
) -> DispatchResult {
ensure_root(origin)?;

let location: Location =
(*location).try_into().map_err(|_| Error::<T>::UnsupportedLocationVersion)?;

let (para_id, agent_id) =
ensure_sibling::<T>(&location).map_err(|_| Error::<T>::InvalidLocation)?;

let asset: Location =
(*asset).try_into().map_err(|_| Error::<T>::UnsupportedLocationVersion)?;

Self::do_register_token(para_id, agent_id, asset, metadata)?;

Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -663,6 +728,41 @@ pub mod pallet {
let secondary_exists = Channels::<T>::contains_key(SECONDARY_GOVERNANCE_CHANNEL);
primary_exists && secondary_exists
}

pub(crate) fn do_register_token(
para_id: ParaId,
agent_id: AgentId,
asset_id: Location,
metadata: AssetRegistrarMetadata,
) -> Result<(), DispatchError> {
// Check that the channel exists
let channel_id: ChannelId = para_id.into();
ensure!(Channels::<T>::contains_key(channel_id), Error::<T>::NoChannel);

// Check that the agent exists
ensure!(Agents::<T>::contains_key(agent_id), Error::<T>::NoAgent);

// Record the token id or fail if it has already been created
let token_id = token_id_of(&asset_id);
ensure!(!Tokens::<T>::contains_key(token_id), Error::<T>::TokenExists);
Tokens::<T>::insert(token_id, asset_id.clone());

let command = Command::AgentExecute {
agent_id,
command: AgentExecuteCommand::RegisterToken {
token_id,
name: metadata.name,
symbol: metadata.symbol,
decimals: metadata.decimals,
},
};
let pays_fee = PaysFee::<T>::Yes(sibling_sovereign_account::<T>(para_id));
Self::send(channel_id, command, pays_fee)?;

Self::deposit_event(Event::<T>::RegisterToken { asset_id, token_id, agent_id });

Ok(())
}
}

impl<T: Config> StaticLookup for Pallet<T> {
Expand All @@ -684,4 +784,13 @@ pub mod pallet {
PricingParameters::<T>::get()
}
}

impl<T: Config> MaybeEquivalence<TokenId, Location> for Pallet<T> {
fn convert(id: &TokenId) -> Option<Location> {
Tokens::<T>::get(id)
}
fn convert_back(_loc: &Location) -> Option<TokenId> {
None
claravanstaden marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
11 changes: 11 additions & 0 deletions bridges/snowbridge/pallets/system/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub trait WeightInfo {
fn force_transfer_native_from_agent() -> Weight;
fn set_token_transfer_fees() -> Weight;
fn set_pricing_parameters() -> Weight;
fn register_token() -> Weight;
}

// For backwards compatibility and tests.
Expand Down Expand Up @@ -246,4 +247,14 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}

fn register_token() -> Weight {
// Proof Size summary in bytes:
// Measured: `256`
// Estimated: `6044`
// Minimum execution time: 45_000_000 picoseconds.
Weight::from_parts(45_000_000, 6044)
.saturating_add(RocksDbWeight::get().reads(5_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
}
17 changes: 15 additions & 2 deletions bridges/snowbridge/primitives/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use frame_support::traits::Contains;
use hex_literal::hex;
use scale_info::TypeInfo;
use sp_core::H256;
use sp_io::hashing::keccak_256;
use sp_io::hashing::{blake2_256, keccak_256};
use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
use sp_std::prelude::*;
use xcm::prelude::{Junction::Parachain, Location};
Expand Down Expand Up @@ -163,4 +163,17 @@ impl DescribeLocation for DescribeHere {

/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on
/// Ethereum which acts as the sovereign account for the Location.
pub type AgentIdOf = HashedDescription<H256, (DescribeHere, DescribeFamily<DescribeAllTerminal>)>;
pub type AgentIdOf =
HashedDescription<AgentId, (DescribeHere, DescribeFamily<DescribeAllTerminal>)>;

#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
pub struct AssetRegistrarMetadata {
pub name: Vec<u8>,
pub symbol: Vec<u8>,
yrong marked this conversation as resolved.
Show resolved Hide resolved
pub decimals: u8,
}

pub type TokenId = H256;
pub fn token_id_of(location: &Location) -> TokenId {
blake2_256(&location.encode()).into()
yrong marked this conversation as resolved.
Show resolved Hide resolved
}
41 changes: 41 additions & 0 deletions bridges/snowbridge/primitives/core/src/outbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,32 @@ mod v1 {
/// The amount of tokens to transfer
amount: u128,
},
RegisterToken {
/// ID for the token
token_id: H256,
/// Name of the token
name: Vec<u8>,
/// Short symbol for the token
symbol: Vec<u8>,
/// Number of decimal places
decimals: u8,
},
MintToken {
yrong marked this conversation as resolved.
Show resolved Hide resolved
/// ID for the token
token_id: H256,
/// The recipient of the newly minted tokens
recipient: H160,
/// The amount of tokens to mint
amount: u128,
},
}

impl AgentExecuteCommand {
fn index(&self) -> u8 {
match self {
AgentExecuteCommand::TransferToken { .. } => 0,
AgentExecuteCommand::RegisterToken { .. } => 1,
AgentExecuteCommand::MintToken { .. } => 2,
}
}

Expand All @@ -259,6 +279,25 @@ mod v1 {
Token::Uint(U256::from(*amount)),
])),
]),
AgentExecuteCommand::RegisterToken { token_id, name, symbol, decimals } =>
ethabi::encode(&[
Token::Uint(self.index().into()),
Token::Bytes(ethabi::encode(&[
Token::FixedBytes(token_id.as_bytes().to_owned()),
Token::String(name.to_owned()),
Token::String(symbol.to_owned()),
Token::Uint(U256::from(*decimals)),
])),
]),
AgentExecuteCommand::MintToken { token_id, recipient, amount } =>
ethabi::encode(&[
Token::Uint(self.index().into()),
Token::Bytes(ethabi::encode(&[
Token::FixedBytes(token_id.as_bytes().to_owned()),
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])),
]),
}
}
}
Expand Down Expand Up @@ -391,6 +430,8 @@ impl GasMeter for ConstantGasMeter {
// * Assume dest account in ERC20 contract does not yet have a storage slot
// * ERC20.transferFrom possibly does other business logic besides updating balances
AgentExecuteCommand::TransferToken { .. } => 100_000,
AgentExecuteCommand::RegisterToken { .. } => 1_500_000,
AgentExecuteCommand::MintToken { .. } => 150_000,
},
Command::Upgrade { initializer, .. } => {
let initializer_max_gas = match *initializer {
Expand Down
Loading
Loading