--- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -19,18 +19,12 @@ // 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 1 // TODO: Unhardcode prefix -const ETH_ACCOUNT_PREFIX: [u8; 16] = [ +const ETH_COLLECTION_PREFIX: [u8; 16] = [ 0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e, ]; -// 0xf8238ccfff8ed887463fd5e00000000100000002 - collection 1, token 2 -// TODO: Unhardcode prefix -const ETH_ACCOUNT_TOKEN_PREFIX: [u8; 12] = [ - 0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0, -]; - pub fn map_eth_to_id(eth: &H160) -> Option { - if eth[0..16] != ETH_ACCOUNT_PREFIX { + if eth[0..16] != ETH_COLLECTION_PREFIX { return None; } let mut id_bytes = [0; 4]; @@ -39,28 +33,7 @@ } pub fn collection_id_to_address(id: CollectionId) -> H160 { let mut out = [0; 20]; - out[0..16].copy_from_slice(Ð_ACCOUNT_PREFIX); + out[0..16].copy_from_slice(Ð_COLLECTION_PREFIX); out[16..20].copy_from_slice(&u32::to_be_bytes(id.0)); - H160(out) -} - -pub fn map_eth_to_token_id(eth: &H160) -> Option<(CollectionId, TokenId)> { - if eth[0..12] != ETH_ACCOUNT_TOKEN_PREFIX { - return None; - } - let mut id_bytes = [0; 4]; - let mut token_id_bytes = [0; 4]; - id_bytes.copy_from_slice(ð[12..16]); - token_id_bytes.copy_from_slice(ð[16..20]); - Some(( - CollectionId(u32::from_be_bytes(id_bytes)), - TokenId(u32::from_be_bytes(token_id_bytes)), - )) -} -pub fn collection_token_id_to_address(id: CollectionId, token: TokenId) -> H160 { - let mut out = [0; 20]; - out[0..12].copy_from_slice(Ð_ACCOUNT_TOKEN_PREFIX); - out[12..16].copy_from_slice(&u32::to_be_bytes(id.0)); - out[16..20].copy_from_slice(&u32::to_be_bytes(token.0)); H160(out) } --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -165,8 +165,10 @@ use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key}; use pallet_evm::account; use dispatch::CollectionDispatch; + use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion}; + use frame_system::pallet_prelude::*; use frame_support::traits::Currency; - use up_data_structs::TokenId; + use up_data_structs::{TokenId, mapping::TokenAddressMapping}; use scale_info::TypeInfo; use up_evm_mapping::CrossAccountId; @@ -185,6 +187,9 @@ type CollectionDispatch: CollectionDispatch; type TreasuryAccountId: Get; + + type EvmTokenAddressMapping: TokenAddressMapping; + type CrossTokenAddressMapping: TokenAddressMapping; } #[pallet::pallet] --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -20,34 +20,23 @@ convert::{TryFrom, TryInto}, fmt, }; -use frame_support::storage::bounded_btree_map::BoundedBTreeMap; -use sp_std::collections::btree_map::BTreeMap; +use frame_support::{ + storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet}, + traits::ConstU16, +}; +use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; #[cfg(feature = "serde")] -pub use serde::{Serialize, Deserialize}; +use serde::{Serialize, Deserialize}; use sp_core::U256; use sp_runtime::{ArithmeticError, sp_std::prelude::Vec}; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -pub use frame_support::{ - BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error, - dispatch::DispatchResult, - ensure, fail, parameter_types, - traits::{ - Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, - Randomness, IsSubType, WithdrawReasons, - }, - weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, - DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight, - WeightToFeePolynomial, DispatchClass, - }, - StorageValue, transactional, - pallet_prelude::ConstU32, -}; +use frame_support::{BoundedVec, traits::ConstU32}; use derivative::Derivative; use scale_info::TypeInfo; +pub mod mapping; mod migration; pub const MAX_DECIMAL_POINTS: DecimalPoints = 30; --- /dev/null +++ b/primitives/data-structs/src/mapping.rs @@ -0,0 +1,63 @@ +use core::marker::PhantomData; + +use sp_core::H160; + +use crate::{CollectionId, TokenId}; +use up_evm_mapping::CrossAccountId; + +pub trait TokenAddressMapping
{ + fn token_to_address(collection: CollectionId, token: TokenId) -> Address; + fn address_to_token(address: &Address) -> Option<(CollectionId, TokenId)>; + fn is_token_address(address: &Address) -> bool; +} + +pub struct EvmTokenAddressMapping; + +/// 0xf8238ccfff8ed887463fd5e00000000100000002 - collection 1, token 2 +const ETH_COLLECTION_TOKEN_PREFIX: [u8; 12] = [ + 0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0, +]; + +impl TokenAddressMapping for EvmTokenAddressMapping { + fn token_to_address(collection: CollectionId, token: TokenId) -> H160 { + let mut out = [0; 20]; + out[0..12].copy_from_slice(Ð_COLLECTION_TOKEN_PREFIX); + out[12..16].copy_from_slice(&u32::to_be_bytes(collection.0)); + out[16..20].copy_from_slice(&u32::to_be_bytes(token.0)); + H160(out) + } + + fn address_to_token(eth: &H160) -> Option<(CollectionId, TokenId)> { + if eth[0..12] != ETH_COLLECTION_TOKEN_PREFIX { + return None; + } + let mut id_bytes = [0; 4]; + let mut token_id_bytes = [0; 4]; + id_bytes.copy_from_slice(ð[12..16]); + token_id_bytes.copy_from_slice(ð[16..20]); + Some(( + CollectionId(u32::from_be_bytes(id_bytes)), + TokenId(u32::from_be_bytes(token_id_bytes)), + )) + } + + fn is_token_address(address: &H160) -> bool { + address[0..12] == ETH_COLLECTION_TOKEN_PREFIX + } +} + +pub struct CrossTokenAddressMapping(PhantomData); + +impl> TokenAddressMapping for CrossTokenAddressMapping { + fn token_to_address(collection: CollectionId, token: TokenId) -> C { + C::from_eth(EvmTokenAddressMapping::token_to_address(collection, token)) + } + + fn address_to_token(address: &C) -> Option<(CollectionId, TokenId)> { + EvmTokenAddressMapping::address_to_token(address.as_eth()) + } + + fn is_token_address(address: &C) -> bool { + EvmTokenAddressMapping::is_token_address(address.as_eth()) + } +} --- a/runtime/opal/src/lib.rs +++ b/runtime/opal/src/lib.rs @@ -66,6 +66,7 @@ WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier, }, }; +use up_data_structs::mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping}; use up_data_structs::{CollectionId, TokenId, CollectionStats, Collection}; // use pallet_contracts::weights::WeightInfo; // #[cfg(any(feature = "std", test))] --- a/tests/src/eth/util/helpers.ts +++ b/tests/src/eth/util/helpers.ts @@ -56,13 +56,27 @@ } } -export function collectionIdToAddress(address: number): string { - if (address >= 0xffffffff || address < 0) throw new Error('id overflow'); +function encodeIntBE(v: number): number[] { + if (v >= 0xffffffff || v < 0) throw new Error('id overflow'); + return [ + v >> 24, + (v >> 16) & 0xff, + (v >> 8) & 0xff, + v & 0xff, + ]; +} + +export function collectionIdToAddress(collection: number): string { const buf = Buffer.from([0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e, - address >> 24, - (address >> 16) & 0xff, - (address >> 8) & 0xff, - address & 0xff, + ...encodeIntBE(collection), + ]); + return Web3.utils.toChecksumAddress('0x' + buf.toString('hex')); +} + +export function tokenIdToAddress(collection: number, token: number): string { + const buf = Buffer.from([0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0, + ...encodeIntBE(collection), + ...encodeIntBE(token), ]); return Web3.utils.toChecksumAddress('0x' + buf.toString('hex')); }