diff --git a/Cargo.lock b/Cargo.lock index de486313f9..8e7f27b407 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1328,6 +1328,21 @@ dependencies = [ "trie-db", ] +[[package]] +name = "cumulus-pallet-xcm-handler" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=rococo-v1#9d89ed653217203810822483ae86fd8867f59620" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "sp-std", + "xcm", + "xcm-executor", +] + [[package]] name = "cumulus-primitives-core" version = "0.1.0" @@ -4516,6 +4531,7 @@ dependencies = [ "account", "author-inherent", "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm-handler", "cumulus-primitives-core", "fp-rpc", "frame-executive", @@ -4544,6 +4560,7 @@ dependencies = [ "parachain-info", "parachain-staking", "parity-scale-codec", + "polkadot-parachain", "precompiles", "serde", "sha3 0.9.1", @@ -4559,6 +4576,11 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder 4.0.0", + "token-factory", + "xcm", + "xcm-builder", + "xcm-executor", + "xtransfer", ] [[package]] @@ -10565,6 +10587,28 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "token-factory" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "fp-evm", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "pallet-evm", + "pallet-sudo", + "pallet-timestamp", + "parity-scale-codec", + "rustc-hex", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "tokio" version = "0.1.22" @@ -11827,6 +11871,23 @@ dependencies = [ "xcm", ] +[[package]] +name = "xtransfer" +version = "0.1.0" +dependencies = [ + "cumulus-pallet-xcm-handler", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", + "token-factory", + "xcm", + "xcm-executor", +] + [[package]] name = "yamux" version = "0.8.1" diff --git a/moonbeam-types-bundle/index.ts b/moonbeam-types-bundle/index.ts index 0bba0eeaee..9672b472a2 100644 --- a/moonbeam-types-bundle/index.ts +++ b/moonbeam-types-bundle/index.ts @@ -210,6 +210,20 @@ export const moonbeamDefinitions = { nominators: "Vec", total: "Balance", }, + TokenId: { + _enum: ["DOT", "KSM", "ACA", "AUSD"], + }, + EvmCall: { + _enum: ["Register", "Mint", "Burn", "TotalIssuance", "BalanceOf"], + }, + AccountId32: "[u8; 32]", + ChainId: { + _enum: ["RelayChain", { Para: "u32" }], + }, + XCurrencyId: { + chain_id: "ChainId", + currency_id: "Vec", + }, SystemInherentData: { validation_data: "PersistedValidationData", relay_chain_state: "StorageProof", diff --git a/pallets/token-dealer/Cargo.toml b/pallets/token-dealer/Cargo.toml deleted file mode 100644 index 1bbc4e6942..0000000000 --- a/pallets/token-dealer/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -authors = ["Parity Technologies "] -edition = "2018" -name = "cumulus-token-dealer" -version = "0.6.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } - -sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } -frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } -frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } - -# Cumulus dependencies -cumulus-upward-message = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "rococo-v1" } -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "rococo-v1" } - -# Polkadot dependencies -polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } - -[features] -default = ["std"] -std = [ - "codec/std", - "cumulus-upward-message/std", - "cumulus-primitives-core/std", - "sp-runtime/std", - "frame-support/std", - "frame-system/std", - "polkadot-parachain/std", -] diff --git a/pallets/token-dealer/src/lib.rs b/pallets/token-dealer/src/lib.rs deleted file mode 100644 index 0ec620c092..0000000000 --- a/pallets/token-dealer/src/lib.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2019-2021 PureStake Inc. -// This file is part of Moonbeam. - -// Moonbeam 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. - -// Moonbeam 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 Moonbeam. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::{ - decl_event, decl_module, dispatch::DispatchResult, - traits::{Currency, ExistenceRequirement, WithdrawReason}, -}; -use frame_system::ensure_signed; - -use codec::{Decode, Encode}; -use cumulus_primitives::{ - relay_chain::DownwardMessage, - xcmp::{XCMPMessageHandler, XCMPMessageSender}, - DownwardMessageHandler, ParaId, UpwardMessageOrigin, UpwardMessageSender, -}; -use cumulus_upward_message::BalancesMessage; -use polkadot_parachain::primitives::AccountIdConversion; - -#[derive(Encode, Decode)] -pub enum XCMPMessage { - /// Transfer tokens to the given account from the Parachain account. - TransferToken(XAccountId, XBalance), -} - -type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; - -type RelayAccountId = sp_runtime::AccountId32; - -/// Configuration trait of this pallet. -pub trait Trait: frame_system::Trait { - /// Event type used by the runtime. - type Event: From> + Into<::Event>; - - /// The sender of upward messages. - type UpwardMessageSender: UpwardMessageSender; - - /// The upward message type used by the Parachain runtime. - type UpwardMessage: codec::Codec + BalancesMessage>; - - /// Currency of the runtime. - type Currency: Currency; - - /// The sender of XCMP messages. - type XCMPMessageSender: XCMPMessageSender>>; -} - -decl_event! { - pub enum Event where - AccountId = ::AccountId, - Balance = BalanceOf - { - /// Transferred tokens to the account on the relay chain. - TransferredTokensToRelayChain(RelayAccountId, Balance), - /// Transferred tokens to the account on request from the relay chain. - TransferredTokensFromRelayChain(AccountId, Balance), - /// Transferred tokens to the account from the given parachain account. - TransferredTokensViaXCMP(ParaId, AccountId, Balance, DispatchResult), - } -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin, system = frame_system { - /// Transfer `amount` of tokens on the relay chain from the Parachain account to - /// the given `dest` account. - #[weight = 10] - fn transfer_tokens_to_relay_chain(origin, dest: RelayAccountId, amount: BalanceOf) { - let who = ensure_signed(origin)?; - - let _ = T::Currency::withdraw( - &who, - amount, - WithdrawReason::Transfer.into(), - ExistenceRequirement::AllowDeath, - )?; - - let msg = ::UpwardMessage::transfer(dest.clone(), amount.clone()); - ::UpwardMessageSender::send_upward_message(&msg, UpwardMessageOrigin::Signed) - .expect("Should not fail; qed"); - - Self::deposit_event(Event::::TransferredTokensToRelayChain(dest, amount)); - } - - /// Transfer `amount` of tokens to another parachain. - #[weight = 10] - fn transfer_tokens_to_parachain_chain( - origin, - para_id: u32, - dest: T::AccountId, - amount: BalanceOf, - ) { - //TODO we don't make sure that the parachain has some tokens on the other parachain. - let who = ensure_signed(origin)?; - - let _ = T::Currency::withdraw( - &who, - amount, - WithdrawReason::Transfer.into(), - ExistenceRequirement::AllowDeath, - )?; - - T::XCMPMessageSender::send_xcmp_message( - para_id.into(), - &XCMPMessage::TransferToken(dest, amount), - ).expect("Should not fail; qed"); - } - - fn deposit_event() = default; - } -} - -/// This is a hack to convert from one generic type to another where we are sure that both are the -/// same type/use the same encoding. -fn convert_hack(input: &impl Encode) -> O { - input.using_encoded(|e| Decode::decode(&mut &e[..]).expect("Must be compatible; qed")) -} - -impl DownwardMessageHandler for Module { - fn handle_downward_message(msg: &DownwardMessage) { - match msg { - DownwardMessage::TransferInto(dest, amount, _) => { - let dest = convert_hack(&dest); - let amount: BalanceOf = convert_hack(amount); - - let _ = T::Currency::deposit_creating(&dest, amount.clone()); - - Self::deposit_event(Event::::TransferredTokensFromRelayChain(dest, amount)); - } - _ => {} - } - } -} - -impl XCMPMessageHandler>> for Module { - fn handle_xcmp_message(src: ParaId, msg: &XCMPMessage>) { - match msg { - XCMPMessage::TransferToken(dest, amount) => { - let para_account = src.clone().into_account(); - - let res = T::Currency::transfer( - ¶_account, - dest, - amount.clone(), - ExistenceRequirement::AllowDeath, - ); - - Self::deposit_event(Event::::TransferredTokensViaXCMP( - src, - dest.clone(), - amount.clone(), - res, - )); - } - } - } -} diff --git a/pallets/token-factory/Cargo.toml b/pallets/token-factory/Cargo.toml new file mode 100644 index 0000000000..366729cda5 --- /dev/null +++ b/pallets/token-factory/Cargo.toml @@ -0,0 +1,43 @@ +[package] +authors = ["PureStake"] +edition = "2018" +name = "token-factory" +version = "0.1.0" +description = "sudo control ERC20 issuance in the EVM" + +[dependencies] +ethereum-types = { version = "0.11.0", default-features = false } +fp-evm = { git = "https://github.com/purestake/frontier", default-features = false, branch = "notlesh-moonbeam-v0.7" } +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +hex-literal = { version = "0.3.1" } +pallet-evm = { git = "https://github.com/purestake/frontier", default-features = false, branch = "notlesh-moonbeam-v0.7" } +pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } +rustc-hex = { version = "2.1.0", default-features = false } +serde = { version = "1.0.101", optional = true } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } + +[features] +default = ["std"] +std = [ + "ethereum-types/std", + "parity-scale-codec/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-evm/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "rustc-hex/std", + "serde", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", + "sp-io/std", +] \ No newline at end of file diff --git a/pallets/token-factory/contract/bytecode.txt b/pallets/token-factory/contract/bytecode.txt new file mode 100644 index 0000000000..3bec98fd54 --- /dev/null +++ b/pallets/token-factory/contract/bytecode.txt @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b50604051620024913803806200249183398181016040528101906200003791906200024f565b818160006200004b6200012560201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3508160049080519060200190620001019291906200012d565b5080600590805190602001906200011a9291906200012d565b505050505062000432565b600033905090565b8280546200013b9062000357565b90600052602060002090601f0160209004810192826200015f5760008555620001ab565b82601f106200017a57805160ff1916838001178555620001ab565b82800160010185558215620001ab579182015b82811115620001aa5782518255916020019190600101906200018d565b5b509050620001ba9190620001be565b5090565b5b80821115620001d9576000816000905550600101620001bf565b5090565b6000620001f4620001ee84620002eb565b620002c2565b9050828152602081018484840111156200020d57600080fd5b6200021a84828562000321565b509392505050565b600082601f8301126200023457600080fd5b815162000246848260208601620001dd565b91505092915050565b600080604083850312156200026357600080fd5b600083015167ffffffffffffffff8111156200027e57600080fd5b6200028c8582860162000222565b925050602083015167ffffffffffffffff811115620002aa57600080fd5b620002b88582860162000222565b9150509250929050565b6000620002ce620002e1565b9050620002dc82826200038d565b919050565b6000604051905090565b600067ffffffffffffffff821115620003095762000308620003f2565b5b620003148262000421565b9050602081019050919050565b60005b838110156200034157808201518184015260208101905062000324565b8381111562000351576000848401525b50505050565b600060028204905060018216806200037057607f821691505b60208210811415620003875762000386620003c3565b5b50919050565b620003988262000421565b810181811067ffffffffffffffff82111715620003ba57620003b9620003f2565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b61204f80620004426000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063715018a6116100a25780639dc29fac116100715780639dc29fac146102cf578063a457c2d7146102eb578063a9059cbb1461031b578063dd62ed3e1461034b578063f2fde38b1461037b57610116565b8063715018a61461026d57806379cc6790146102775780638da5cb5b1461029357806395d89b41146102b157610116565b8063313ce567116100e9578063313ce567146101b757806339509351146101d557806340c10f191461020557806342966c681461022157806370a082311461023d57610116565b806306fdde031461011b578063095ea7b31461013957806318160ddd1461016957806323b872dd14610187575b600080fd5b610123610397565b604051610130919061186d565b60405180910390f35b610153600480360381019061014e9190611596565b610429565b6040516101609190611852565b60405180910390f35b610171610447565b60405161017e9190611a2f565b60405180910390f35b6101a1600480360381019061019c9190611547565b610451565b6040516101ae9190611852565b60405180910390f35b6101bf610552565b6040516101cc9190611a4a565b60405180910390f35b6101ef60048036038101906101ea9190611596565b61055b565b6040516101fc9190611852565b60405180910390f35b61021f600480360381019061021a9190611596565b610607565b005b61023b600480360381019061023691906115d2565b610691565b005b610257600480360381019061025291906114e2565b6106a5565b6040516102649190611a2f565b60405180910390f35b6102756106ee565b005b610291600480360381019061028c9190611596565b610828565b005b61029b6108ac565b6040516102a89190611837565b60405180910390f35b6102b96108d5565b6040516102c6919061186d565b60405180910390f35b6102e960048036038101906102e49190611596565b610967565b005b61030560048036038101906103009190611596565b6109f1565b6040516103129190611852565b60405180910390f35b61033560048036038101906103309190611596565b610ae5565b6040516103429190611852565b60405180910390f35b6103656004803603810190610360919061150b565b610b03565b6040516103729190611a2f565b60405180910390f35b610395600480360381019061039091906114e2565b610b8a565b005b6060600480546103a690611b93565b80601f01602080910402602001604051908101604052809291908181526020018280546103d290611b93565b801561041f5780601f106103f45761010080835404028352916020019161041f565b820191906000526020600020905b81548152906001019060200180831161040257829003601f168201915b5050505050905090565b600061043d610436610d33565b8484610d3b565b6001905092915050565b6000600354905090565b600061045e848484610f06565b6000600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006104a9610d33565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610529576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105209061192f565b60405180910390fd5b61054685610535610d33565b85846105419190611ad7565b610d3b565b60019150509392505050565b60006012905090565b60006105fd610568610d33565b848460026000610576610d33565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546105f89190611a81565b610d3b565b6001905092915050565b61060f610d33565b73ffffffffffffffffffffffffffffffffffffffff1661062d6108ac565b73ffffffffffffffffffffffffffffffffffffffff1614610683576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067a9061194f565b60405180910390fd5b61068d8282611188565b5050565b6106a261069c610d33565b826112dd565b50565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6106f6610d33565b73ffffffffffffffffffffffffffffffffffffffff166107146108ac565b73ffffffffffffffffffffffffffffffffffffffff161461076a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107619061194f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600061083b83610836610d33565b610b03565b905081811015610880576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108779061196f565b60405180910390fd5b61089d8361088c610d33565b84846108989190611ad7565b610d3b565b6108a783836112dd565b505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600580546108e490611b93565b80601f016020809104026020016040519081016040528092919081815260200182805461091090611b93565b801561095d5780601f106109325761010080835404028352916020019161095d565b820191906000526020600020905b81548152906001019060200180831161094057829003601f168201915b5050505050905090565b61096f610d33565b73ffffffffffffffffffffffffffffffffffffffff1661098d6108ac565b73ffffffffffffffffffffffffffffffffffffffff16146109e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109da9061194f565b60405180910390fd5b6109ed82826112dd565b5050565b60008060026000610a00610d33565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610abd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ab4906119ef565b60405180910390fd5b610ada610ac8610d33565b858584610ad59190611ad7565b610d3b565b600191505092915050565b6000610af9610af2610d33565b8484610f06565b6001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610b92610d33565b73ffffffffffffffffffffffffffffffffffffffff16610bb06108ac565b73ffffffffffffffffffffffffffffffffffffffff1614610c06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bfd9061194f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610c76576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c6d906118cf565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610dab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da2906119cf565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610e1b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e12906118ef565b60405180910390fd5b80600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051610ef99190611a2f565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610f76576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f6d906119af565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610fe6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fdd9061188f565b60405180910390fd5b610ff18383836114b3565b6000600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611078576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161106f9061190f565b60405180910390fd5b81816110849190611ad7565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546111169190611a81565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161117a9190611a2f565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156111f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ef90611a0f565b60405180910390fd5b611204600083836114b3565b80600360008282546112169190611a81565b9250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461126c9190611a81565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516112d19190611a2f565b60405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561134d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113449061198f565b60405180910390fd5b611359826000836114b3565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156113e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113d7906118af565b60405180910390fd5b81816113ec9190611ad7565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008282546114419190611ad7565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516114a69190611a2f565b60405180910390a3505050565b505050565b6000813590506114c781611feb565b92915050565b6000813590506114dc81612002565b92915050565b6000602082840312156114f457600080fd5b6000611502848285016114b8565b91505092915050565b6000806040838503121561151e57600080fd5b600061152c858286016114b8565b925050602061153d858286016114b8565b9150509250929050565b60008060006060848603121561155c57600080fd5b600061156a868287016114b8565b935050602061157b868287016114b8565b925050604061158c868287016114cd565b9150509250925092565b600080604083850312156115a957600080fd5b60006115b7858286016114b8565b92505060206115c8858286016114cd565b9150509250929050565b6000602082840312156115e457600080fd5b60006115f2848285016114cd565b91505092915050565b61160481611b0b565b82525050565b61161381611b1d565b82525050565b600061162482611a65565b61162e8185611a70565b935061163e818560208601611b60565b61164781611c23565b840191505092915050565b600061165f602383611a70565b915061166a82611c34565b604082019050919050565b6000611682602283611a70565b915061168d82611c83565b604082019050919050565b60006116a5602683611a70565b91506116b082611cd2565b604082019050919050565b60006116c8602283611a70565b91506116d382611d21565b604082019050919050565b60006116eb602683611a70565b91506116f682611d70565b604082019050919050565b600061170e602883611a70565b915061171982611dbf565b604082019050919050565b6000611731602083611a70565b915061173c82611e0e565b602082019050919050565b6000611754602483611a70565b915061175f82611e37565b604082019050919050565b6000611777602183611a70565b915061178282611e86565b604082019050919050565b600061179a602583611a70565b91506117a582611ed5565b604082019050919050565b60006117bd602483611a70565b91506117c882611f24565b604082019050919050565b60006117e0602583611a70565b91506117eb82611f73565b604082019050919050565b6000611803601f83611a70565b915061180e82611fc2565b602082019050919050565b61182281611b49565b82525050565b61183181611b53565b82525050565b600060208201905061184c60008301846115fb565b92915050565b6000602082019050611867600083018461160a565b92915050565b600060208201905081810360008301526118878184611619565b905092915050565b600060208201905081810360008301526118a881611652565b9050919050565b600060208201905081810360008301526118c881611675565b9050919050565b600060208201905081810360008301526118e881611698565b9050919050565b60006020820190508181036000830152611908816116bb565b9050919050565b60006020820190508181036000830152611928816116de565b9050919050565b6000602082019050818103600083015261194881611701565b9050919050565b6000602082019050818103600083015261196881611724565b9050919050565b6000602082019050818103600083015261198881611747565b9050919050565b600060208201905081810360008301526119a88161176a565b9050919050565b600060208201905081810360008301526119c88161178d565b9050919050565b600060208201905081810360008301526119e8816117b0565b9050919050565b60006020820190508181036000830152611a08816117d3565b9050919050565b60006020820190508181036000830152611a28816117f6565b9050919050565b6000602082019050611a446000830184611819565b92915050565b6000602082019050611a5f6000830184611828565b92915050565b600081519050919050565b600082825260208201905092915050565b6000611a8c82611b49565b9150611a9783611b49565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115611acc57611acb611bc5565b5b828201905092915050565b6000611ae282611b49565b9150611aed83611b49565b925082821015611b0057611aff611bc5565b5b828203905092915050565b6000611b1682611b29565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b83811015611b7e578082015181840152602081019050611b63565b83811115611b8d576000848401525b50505050565b60006002820490506001821680611bab57607f821691505b60208210811415611bbf57611bbe611bf4565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000601f19601f8301169050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b611ff481611b0b565b8114611fff57600080fd5b50565b61200b81611b49565b811461201657600080fd5b5056fea2646970667358221220ebdfee37207c65b6959cdf9a6ebf426763546f84e56c8cb909bf2b3b640eaea464736f6c634300080200330000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/pallets/token-factory/contract/contract.sol b/pallets/token-factory/contract/contract.sol new file mode 100644 index 0000000000..d963d5fc76 --- /dev/null +++ b/pallets/token-factory/contract/contract.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.0/contracts/access/Ownable.sol"; +import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.0/contracts/token/ERC20/extensions/ERC20Burnable.sol"; + +/** + * @dev {ERC20} token, including: + * + * The account that deploys the contract can mint and burn. + */ +contract ERC20MinterBurner is Ownable, ERC20Burnable { + /** + * @dev + * + * See {ERC20-constructor}. + */ + constructor(string memory name, string memory symbol) ERC20(name, symbol) {} + + /** + * @dev Creates `amount` new tokens for `to`. + * + * See {ERC20-_mint}. + * + * Requirements: + * + * - the caller must be the account that deployed the contract + */ + function mint(address to, uint256 amount) public virtual onlyOwner { + _mint(to, amount); + } + + /** + * @dev Burns `amount` tokens from `from` + * + * See {ERC20Burnable-_burn}. + * + * Requirements: + * + * - the caller must be the account that deployed the contract + */ + function burn(address from, uint256 amount) public virtual onlyOwner { + _burn(from, amount); + } +} diff --git a/pallets/token-factory/src/lib.rs b/pallets/token-factory/src/lib.rs new file mode 100644 index 0000000000..9c2407c512 --- /dev/null +++ b/pallets/token-factory/src/lib.rs @@ -0,0 +1,518 @@ +// Copyright 2019-2020 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Pallet for registering, minting, and burning ERC20 tokens (in EVM from runtime) + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::pallet; + +pub use pallet::*; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +#[pallet] +pub mod pallet { + use ethereum_types::BigEndianHash; + use fp_evm::ExecutionInfo; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use pallet_evm::{AddressMapping, ExitReason, Runner}; + use parity_scale_codec::{Decode, Encode, FullCodec}; + use rustc_hex::FromHex; + #[cfg(feature = "std")] + use serde::{Deserialize, Serialize}; + use sp_core::{H160, H256, U256}; + use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Convert, MaybeSerializeDeserialize, Zero}, + DispatchError, RuntimeDebug, SaturatedConversion, + }; + use sp_std::{convert::TryFrom, fmt::Debug, vec::Vec}; + + /// ERC20PresetMinterBurner contract bytecode + const CONTRACT_BYTECODE: &str = include_str!("../contract/bytecode.txt"); + + #[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord)] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[non_exhaustive] + /// The name and unique ID for each token registered in `token-factory` + pub enum Ticker { + DOT = 0, + KSM = 1, + ACA = 2, + AUSD = 3, + } + + impl TryFrom for Ticker { + type Error = (); + + fn try_from(v: u8) -> Result { + match v { + 0 => Ok(Ticker::DOT), + 1 => Ok(Ticker::KSM), + 2 => Ok(Ticker::ACA), + 3 => Ok(Ticker::AUSD), + _ => Err(()), + } + } + } + + #[derive(sp_runtime::RuntimeDebug, PartialEq, Eq)] + /// The supported currency types + pub enum CurrencyId { + /// The local instance of `balances` pallet, default GLMR + Native, + /// Token registered in `token-factory` pallet + Token(Ticker), + } + + impl TryFrom> for CurrencyId { + type Error = (); + fn try_from(v: Vec) -> Result { + match v.as_slice() { + b"GLMR" => Ok(CurrencyId::Native), + b"DOT" => Ok(CurrencyId::Token(Ticker::DOT)), + b"KSM" => Ok(CurrencyId::Token(Ticker::KSM)), + b"ACA" => Ok(CurrencyId::Token(Ticker::ACA)), + b"AUSD" => Ok(CurrencyId::Token(Ticker::AUSD)), + _ => Err(()), + } + } + } + + impl From for Vec { + fn from(c: CurrencyId) -> Vec { + match c { + CurrencyId::Native => b"GLMR".to_vec(), + CurrencyId::Token(Ticker::DOT) => b"DOT".to_vec(), + CurrencyId::Token(Ticker::KSM) => b"KSM".to_vec(), + CurrencyId::Token(Ticker::ACA) => b"ACA".to_vec(), + CurrencyId::Token(Ticker::AUSD) => b"AUSD".to_vec(), + } + } + } + + #[test] + fn currency_as_vec() { + let expect: CurrencyId = CurrencyId::try_from([71, 76, 77, 82].to_vec()).unwrap(); + assert_eq!(expect, CurrencyId::Native); + let expect: CurrencyId = CurrencyId::try_from([68, 79, 84].to_vec()).unwrap(); + assert_eq!(expect, CurrencyId::Token(Ticker::DOT)); + let expect: CurrencyId = CurrencyId::try_from([75, 83, 77].to_vec()).unwrap(); + assert_eq!(expect, CurrencyId::Token(Ticker::KSM)); + let expect: CurrencyId = CurrencyId::try_from([65, 67, 65].to_vec()).unwrap(); + assert_eq!(expect, CurrencyId::Token(Ticker::ACA)); + let expect: CurrencyId = CurrencyId::try_from([65, 85, 83, 68].to_vec()).unwrap(); + assert_eq!(expect, CurrencyId::Token(Ticker::AUSD)); + } + + /// The ERC token factory pallet + #[pallet::pallet] + pub struct Pallet(PhantomData); + + /// Configuration trait of this pallet. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_evm::Config + pallet_sudo::Config { + /// Overarching event type + type Event: From> + IsType<::Event>; + /// Balances type + type Balance: Parameter + + Member + + AtLeast32BitUnsigned + + Default + + Copy + + MaybeSerializeDeserialize + + Into; + /// Token identifier + type TokenId: Clone + Copy + FullCodec + Debug + PartialEq + Ord + MaybeSerializeDeserialize; + /// Convert from AccountId to H160, is identity map for Moonbeam + type AccountToH160: Convert; + } + + #[pallet::error] + pub enum Error { + /// TokenID is already claimed + IdClaimed, + /// TokenID does not exist so cannot interact with it + IdNotClaimed, + /// Not enough balance to burn + NotEnoughBalanceToBurn, + } + + #[derive(PartialEq, Clone, Copy, Encode, Decode, sp_runtime::RuntimeDebug)] + pub enum EvmCall { + Register, + Mint, + Burn, + TotalIssuance, + BalanceOf, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// Token register success [token_id, contract_address] + Registered(T::TokenId, H160), + /// Mint token success. [token_id, who, amount] + Minted(T::TokenId, T::AccountId, T::Balance), + /// Burn token success. [token_id, who, amount] + Burned(T::TokenId, T::AccountId, T::Balance), + /// Destroy all tokens success. [token_id, amount] + DestroyedAll(T::TokenId, T::Balance), + /// Call failed with exit reason [call, reason] + EvmCallFailed(EvmCall, ExitReason), + } + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::storage] + #[pallet::getter(fn nonce)] + pub type Nonce = StorageValue<_, U256, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn tokens)] + pub type Tokens = StorageValue<_, Vec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn contract_address)] + pub type ContractAddress = + StorageMap<_, Twox64Concat, T::TokenId, H160, OptionQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub nonce: U256, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + Self { + nonce: U256::zero(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + >::put(self.nonce); + } + } + + #[pallet::call] + impl Pallet { + #[pallet::weight(0)] + pub fn register_token(origin: OriginFor, id: T::TokenId) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + ensure!(!Self::exists(&id), Error::::IdClaimed); + let contract = FromHex::from_hex(CONTRACT_BYTECODE) + .expect("Static smart contract is formatted incorrectly (should be hex)"); + let mut nonce = >::get(); + // deploy contract with sudo as MINTER_ROLE and BURNER_ROLE (without minting) + match T::Runner::create( + // from: H160 + Self::sudo_caller(), + // input: Vec + contract, + // value: U256 + U256::zero(), + // gas limit: U256 + U256::from(0x10000000).low_u64(), + // gas price: U256 + Some(U256::from(1)), + // nonce: Option + Some(nonce), + // config: EvmConfig + T::config(), + )? { + ExecutionInfo { + exit_reason: ExitReason::Succeed(_), + value: address, + .. + } => { + // insert contract + >::insert(&id, address.clone()); + // add to tokens + >::mutate(|list| { + if let Err(loc) = list.binary_search(&id) { + list.insert(loc, id.clone()); + } + }); + Self::deposit_event(Event::Registered(id, address)); + } + ExecutionInfo { + exit_reason: reason, + .. + } => { + Self::deposit_event(Event::EvmCallFailed(EvmCall::Register, reason)); + } + } + // increment nonce + nonce += U256::from(1); + >::put(nonce); + Ok(().into()) + } + + #[pallet::weight(0)] + pub fn destroy_all(origin: OriginFor, id: T::TokenId) -> DispatchResultWithPostInfo { + frame_system::ensure_root(origin)?; + let amount_destroyed = Self::total_issuance(id)?; + // clear storage and free id, tokens associated with the contract are no longer + // recognized by the cross-chain transfer system + >::remove(&id); + >::mutate(|list| { + if let Ok(loc) = list.binary_search(&id) { + list.remove(loc); + } + }); + Self::deposit_event(Event::DestroyedAll(id, amount_destroyed)); + Ok(().into()) + } + } + + impl Pallet { + fn sudo_caller() -> H160 { + T::AccountToH160::convert(>::key()) + } + } + + /// Isolates behavior for minting/burning tokens from registration + pub trait TokenMinter { + fn exists(id: &Id) -> bool; + // setters + fn mint(id: Id, who: Account, amount: Balance) -> DispatchResultWithPostInfo; + fn burn(id: Id, who: Account, amount: Balance) -> DispatchResultWithPostInfo; + // getters + fn total_issuance(id: Id) -> Result; + fn balance_of(id: Id, who: Account) -> Result; + } + + impl TokenMinter for Pallet { + fn exists(id: &T::TokenId) -> bool { + >::get(id).is_some() + } + fn mint(id: T::TokenId, who: H160, amount: T::Balance) -> DispatchResultWithPostInfo { + let address = >::get(&id).ok_or(Error::::IdNotClaimed)?; + let mut nonce = >::get(); + let mut input = hex_literal::hex!("40c10f19").to_vec(); + // append address + input.extend_from_slice(H256::from(who.clone()).as_bytes()); + // append amount + input.extend_from_slice( + H256::from_uint(&U256::from(amount.saturated_into::())).as_bytes(), + ); + // call evm + match T::Runner::call( + // source: H160 + Self::sudo_caller(), + // target + address, + // input: Vec + input, + // value: U256 + U256::zero(), + // gas limit: U256 + U256::from(0x10000000).low_u64(), + // gas price: U256 + Some(U256::from(1)), + // nonce: Option + Some(nonce), + // config: EvmConfig + T::config(), + )? { + ExecutionInfo { + exit_reason: ExitReason::Succeed(_), + .. + } => { + Self::deposit_event(Event::Minted( + id, + T::AddressMapping::into_account_id(who), + amount, + )); + } + ExecutionInfo { + exit_reason: reason, + .. + } => { + Self::deposit_event(Event::EvmCallFailed(EvmCall::Mint, reason)); + } + } + // increment nonce + nonce += U256::from(1); + >::put(nonce); + Ok(().into()) + } + fn burn(id: T::TokenId, who: H160, amount: T::Balance) -> DispatchResultWithPostInfo { + let address = >::get(&id).ok_or(Error::::IdNotClaimed)?; + // check that `who` has at least `amount` of `id` token + let balance = Self::balance_of(id, who)?; + let amount_to_burn = if amount > balance { balance } else { amount }; + ensure!( + amount_to_burn > T::Balance::zero(), + Error::::NotEnoughBalanceToBurn + ); + let mut nonce = >::get(); + let mut input = hex_literal::hex!("9dc29fac").to_vec(); + // append address + input.extend_from_slice(H256::from(who.clone()).as_bytes()); + // append amount + input.extend_from_slice( + H256::from_uint(&U256::from(amount_to_burn.saturated_into::())).as_bytes(), + ); + match T::Runner::call( + // source: H160 + Self::sudo_caller(), + // target + address, + // input: Vec + input, + // value: U256 + U256::zero(), + // gas limit: U256 + U256::from(0x10000000).low_u64(), + // gas price: U256 + Some(U256::from(1)), + // nonce: Option + Some(nonce), + // config: EvmConfig + T::config(), + )? { + ExecutionInfo { + exit_reason: ExitReason::Succeed(_), + .. + } => { + Self::deposit_event(Event::Burned( + id, + T::AddressMapping::into_account_id(who), + amount_to_burn, + )); + } + ExecutionInfo { + exit_reason: reason, + .. + } => { + Self::deposit_event(Event::EvmCallFailed(EvmCall::Burn, reason)); + } + } + // increment nonce + nonce += U256::from(1); + >::put(nonce); + Ok(().into()) + } + /// Gets total issuance for the given token if it exists in local evm instance + fn total_issuance(id: T::TokenId) -> Result { + let address = >::get(id).ok_or(Error::::IdNotClaimed)?; + let mut nonce = >::get(); + // first 4 bytes of hex output of Sha3("totalSupply()") + let input = hex_literal::hex!("18160ddd").to_vec(); + match T::Runner::call( + // source: H160 + Self::sudo_caller(), + // target + address, + // input: Vec + input, + // value: U256 + U256::zero(), + // gas limit: U256 + U256::from(0x10000000).low_u64(), + // gas price: U256 + Some(U256::from(1)), + // nonce: Option + Some(nonce), + // config: EvmConfig + T::config(), + ) { + Ok(ExecutionInfo { + exit_reason: ExitReason::Succeed(_), + value: result, + .. + }) => { + // increment nonce + nonce += U256::from(1); + >::put(nonce); + let value = U256::from(result.as_slice()).saturated_into::(); + Ok(value.saturated_into::()) + } + Ok(ExecutionInfo { + exit_reason: reason, + .. + }) => { + // increment nonce + nonce += U256::from(1); + >::put(nonce); + Self::deposit_event(Event::EvmCallFailed(EvmCall::TotalIssuance, reason)); + Ok(T::Balance::zero()) + } + Err(e) => Err(e.into()), + } + } + /// Gets token balance for the account + fn balance_of(id: T::TokenId, who: H160) -> Result { + let address = >::get(id).ok_or(Error::::IdNotClaimed)?; + let mut nonce = >::get(); + // first 4 bytes of hex output of Sha3("balanceOf(address)") + let mut input = hex_literal::hex!("70a08231").to_vec(); + // append address + input.extend_from_slice(H256::from(who).as_bytes()); + match T::Runner::call( + // source: H160 + Self::sudo_caller(), + // target + address, + // input: Vec + input, + // value: U256 + U256::zero(), + // gas limit: U256 + U256::from(0x10000000).low_u64(), + // gas price: U256 + Some(U256::from(1)), + // nonce: Option + Some(nonce), + // config: EvmConfig + T::config(), + ) { + Ok(ExecutionInfo { + exit_reason: ExitReason::Succeed(_), + value: result, + .. + }) => { + // increment nonce + nonce += U256::from(1); + >::put(nonce); + let value = U256::from(result.as_slice()).saturated_into::(); + return Ok(value.saturated_into::()); + } + Ok(ExecutionInfo { + exit_reason: reason, + .. + }) => { + // increment nonce + nonce += U256::from(1); + >::put(nonce); + Self::deposit_event(Event::EvmCallFailed(EvmCall::BalanceOf, reason)); + Ok(T::Balance::zero()) + } + Err(e) => Err(e.into()), + } + } + } +} diff --git a/pallets/token-factory/src/mock.rs b/pallets/token-factory/src/mock.rs new file mode 100644 index 0000000000..b365942437 --- /dev/null +++ b/pallets/token-factory/src/mock.rs @@ -0,0 +1,203 @@ +// Copyright 2019-2020 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Token Factory Mock Runtime +use crate as token_factory; +use frame_support::parameter_types; +use sp_core::{H160, H256}; +use sp_io::TestExternalities; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + ModuleId, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Evm: pallet_evm::{Pallet, Call, Storage, Event}, + TokenFactory: token_factory::{Pallet, Call, Storage, Event}, + } +); + +pub struct BlockEverything; +impl frame_support::traits::Filter for BlockEverything { + fn filter(_: &Call) -> bool { + false + } +} + +parameter_types! { + pub const MinimumPeriod: u64 = 6000 / 2; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +pub struct FixedGasPrice; +impl pallet_evm::FeeCalculator for FixedGasPrice { + fn min_gas_price() -> sp_core::U256 { + 1.into() + } +} + +impl pallet_sudo::Config for Test { + type Event = Event; + type Call = Call; +} + +parameter_types! { + pub const TransactionByteFee: u64 = 1; + pub const ChainId: u64 = 42; + pub const EVMModuleId: ModuleId = ModuleId(*b"py/evmpa"); +} + +impl pallet_evm::Config for Test { + type FeeCalculator = FixedGasPrice; + type GasWeightMapping = (); + type CallOrigin = pallet_evm::EnsureAddressSame; + type WithdrawOrigin = pallet_evm::EnsureAddressSame; + type AddressMapping = pallet_evm::IdentityAddressMapping; + type Currency = Balances; + type Event = Event; + type Precompiles = (); + type Runner = pallet_evm::runner::stack::Runner; + type ChainId = ChainId; + type OnChargeTransaction = (); +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} +impl frame_system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = Call; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); +} +type Balance = u64; +type AccountId = H160; +parameter_types! { + pub const MaxLocks: u32 = 50; + pub const ExistentialDeposit: u64 = 500; +} +impl pallet_balances::Config for Test { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} +pub struct AccountToH160; +impl sp_runtime::traits::Convert for AccountToH160 { + fn convert(from: H160) -> H160 { + from + } +} +impl token_factory::Config for Test { + type Event = Event; + type Balance = Balance; + type TokenId = u8; + type AccountToH160 = AccountToH160; +} + +pub(crate) fn last_event() -> Event { + System::events().pop().expect("Event expected").event +} + +pub(crate) fn root_address() -> H160 { + use sp_std::str::FromStr; + H160::from_str("6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").unwrap() +} + +pub(crate) fn alice() -> H160 { + use sp_std::str::FromStr; + H160::from_str("1000000000000000000000000000000000000001").unwrap() +} + +pub(crate) fn bob() -> H160 { + use sp_std::str::FromStr; + H160::from_str("1000000000000000000000000000000000000002").unwrap() +} + +pub(crate) fn charlie() -> H160 { + use sp_std::str::FromStr; + H160::from_str("1000000000000000000000000000000000000003").unwrap() +} + +pub(crate) fn deploy_addresses() -> Vec { + use sp_std::str::FromStr; + vec![ + H160::from_str("c2bf5f29a4384b1ab0c063e1c666f02121b6084a").unwrap(), + H160::from_str("5c4242beb94de30b922f57241f1d02f36e906915").unwrap(), + H160::from_str("42e2ee7ba8975c473157634ac2af4098190fc741").unwrap(), + H160::from_str("f8cef78e923919054037a1d03662bbd884ff4edf").unwrap(), + H160::from_str("e573bca813c741229ffb2488f7856c6caa841041").unwrap(), + H160::from_str("bb0cc0fb3e0c06725c67167501f850b4900d6db5").unwrap(), + H160::from_str("fe5d3c52f7ee9aa32a69b96bfbb088ba0bcd8efc").unwrap(), + H160::from_str("92496871560a01551e1b4fd04540d7a519d5c19e").unwrap(), + H160::from_str("63a1519ee99d1121780fffa1726ed2ecc6d1611b").unwrap(), + H160::from_str("dc552396caec809752fed0c5e23fd3983766e758").unwrap(), + ] +} + +pub(crate) fn genesis(balances: Vec<(AccountId, Balance)>) -> TestExternalities { + let mut storage = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + let genesis = pallet_balances::GenesisConfig:: { balances }; + genesis.assimilate_storage(&mut storage).unwrap(); + let genesis = pallet_sudo::GenesisConfig:: { + key: root_address(), + }; + genesis.assimilate_storage(&mut storage).unwrap(); + let mut ext = sp_io::TestExternalities::from(storage); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/pallets/token-factory/src/tests.rs b/pallets/token-factory/src/tests.rs new file mode 100644 index 0000000000..1e0631b501 --- /dev/null +++ b/pallets/token-factory/src/tests.rs @@ -0,0 +1,172 @@ +// Copyright 2019-2020 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Token Factory Unit Tests +use crate::mock::{ + alice, bob, charlie, deploy_addresses, genesis, last_event, root_address, Event as TestEvent, + Origin, Test, TokenFactory, +}; +use crate::{pallet::TokenMinter, Error, Event}; +use frame_support::{assert_err, assert_noop, assert_ok}; + +#[test] +fn registration() { + genesis(vec![(root_address(), 5000000000000)]).execute_with(|| { + let tokens_to_register = 10u8; + let addresses = deploy_addresses(); + for i in 0..tokens_to_register { + let address = addresses[i as usize]; + assert!(!TokenFactory::exists(&i)); + assert_ok!(TokenFactory::register_token(Origin::root(), i)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Registered(i, address)) + ); + assert!(TokenFactory::exists(&i)); + assert_eq!(TokenFactory::contract_address(i).unwrap(), address); + } + }); +} + +#[test] +fn de_registration() { + genesis(vec![(root_address(), 5000000000000)]).execute_with(|| { + assert_noop!( + TokenFactory::destroy_all(Origin::root(), 1u8), + Error::::IdNotClaimed + ); + assert_ok!(TokenFactory::register_token(Origin::root(), 1u8)); + assert_ok!(TokenFactory::destroy_all(Origin::root(), 1u8)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::DestroyedAll(1u8, 0u64)) + ); + assert!(!TokenFactory::exists(&1u8)); + assert!(TokenFactory::contract_address(1u8).is_none()); + }); +} + +#[test] +fn minting() { + genesis(vec![(root_address(), 5000000000000)]).execute_with(|| { + assert_ok!(TokenFactory::register_token(Origin::root(), 1u8)); + assert_noop!( + TokenFactory::mint(0u8, alice(), 10000), + Error::::IdNotClaimed + ); + assert_ok!(TokenFactory::mint(1u8, alice(), 10000)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Minted(1, alice(), 10000)) + ); + assert_ok!(TokenFactory::mint(1u8, bob(), 10000)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Minted(1, bob(), 10000)) + ); + assert_ok!(TokenFactory::mint(1u8, charlie(), 10000)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Minted(1, charlie(), 10000)) + ); + assert_eq!(TokenFactory::total_issuance(1u8).unwrap(), 30000); + assert_eq!(TokenFactory::balance_of(1u8, alice()).unwrap(), 10000); + assert_eq!(TokenFactory::balance_of(1u8, bob()).unwrap(), 10000); + assert_eq!(TokenFactory::balance_of(1u8, charlie()).unwrap(), 10000); + }); +} + +#[test] +fn burning() { + genesis(vec![(root_address(), 5000000000000)]).execute_with(|| { + assert_ok!(TokenFactory::register_token(Origin::root(), 1u8)); + assert_noop!( + TokenFactory::burn(0u8, alice(), 10000), + Error::::IdNotClaimed + ); + // not a noop because we still iterate the nonce when we get balance_of before burning + assert_err!( + TokenFactory::burn(1u8, alice(), 5000), + Error::::NotEnoughBalanceToBurn + ); + assert_ok!(TokenFactory::mint(1u8, alice(), 10000)); + assert_err!( + TokenFactory::burn(1u8, bob(), 5000), + Error::::NotEnoughBalanceToBurn + ); + assert_ok!(TokenFactory::mint(1u8, bob(), 10000)); + assert_err!( + TokenFactory::burn(1u8, charlie(), 5000), + Error::::NotEnoughBalanceToBurn + ); + assert_ok!(TokenFactory::mint(1u8, charlie(), 10000)); + assert_ok!(TokenFactory::burn(1u8, alice(), 5000)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Burned(1, alice(), 5000)) + ); + assert_ok!(TokenFactory::burn(1u8, bob(), 5000)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Burned(1, bob(), 5000)) + ); + assert_ok!(TokenFactory::burn(1u8, charlie(), 10000)); + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Burned(1, charlie(), 10000)) + ); + assert_eq!(TokenFactory::total_issuance(1u8).unwrap(), 10000); + assert_eq!(TokenFactory::balance_of(1u8, alice()).unwrap(), 5000); + assert_eq!(TokenFactory::balance_of(1u8, bob()).unwrap(), 5000); + assert_eq!(TokenFactory::balance_of(1u8, charlie()).unwrap(), 0); + }); +} + +#[test] +fn get_total_supply() { + genesis(vec![(root_address(), 5000000000000)]).execute_with(|| { + assert_ok!(TokenFactory::register_token(Origin::root(), 1u8)); + assert_ok!(TokenFactory::total_issuance(1u8)); + // implies that the error event was not emitted in total issuance call + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Registered(1u8, deploy_addresses()[0])) + ); + assert_eq!(TokenFactory::total_issuance(1u8).unwrap(), 0u64); + assert_noop!( + TokenFactory::total_issuance(2u8), + Error::::IdNotClaimed + ); + }); +} + +#[test] +fn get_balance_of() { + genesis(vec![(root_address(), 5000000000000)]).execute_with(|| { + assert_ok!(TokenFactory::register_token(Origin::root(), 1u8)); + assert_ok!(TokenFactory::balance_of(1u8, root_address())); + // implies that the error event was not emitted in total issuance call + assert_eq!( + last_event(), + TestEvent::token_factory(Event::Registered(1u8, deploy_addresses()[0])) + ); + assert_eq!(TokenFactory::balance_of(1u8, root_address()).unwrap(), 0u64); + assert_noop!( + TokenFactory::balance_of(2u8, root_address()), + Error::::IdNotClaimed + ); + }); +} diff --git a/pallets/xtransfer/Cargo.toml b/pallets/xtransfer/Cargo.toml new file mode 100644 index 0000000000..1b4103c17a --- /dev/null +++ b/pallets/xtransfer/Cargo.toml @@ -0,0 +1,37 @@ +[package] +authors = ["PureStake"] +edition = "2018" +name = "xtransfer" +version = '0.1.0' +description = "interface for handling cross-chain transfers to relay chain or other parachains" + +[dependencies] +parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } + +token-factory = { path = "../token-factory", default-features = false } + +xcm = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } +cumulus-pallet-xcm-handler = { git = "https://github.com/paritytech/cumulus", branch = "rococo-v1", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "rococo-v1", default-features = false } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", + "token-factory/std", + "xcm-executor/std", + "cumulus-pallet-xcm-handler/std", + "cumulus-primitives-core/std", +] diff --git a/pallets/xtransfer/src/adapter.rs b/pallets/xtransfer/src/adapter.rs new file mode 100644 index 0000000000..d3e0c51f09 --- /dev/null +++ b/pallets/xtransfer/src/adapter.rs @@ -0,0 +1,136 @@ +// Copyright 2019-2020 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Currency adapter implementation for processing cross-chain messages + +use crate::support::CurrencyIdConversion; +use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; +use sp_std::{marker::PhantomData, prelude::*, result}; +use token_factory::{CurrencyId, Ticker}; +use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult}; +use xcm_executor::traits::{LocationConversion, MatchesFungible, TransactAsset}; + +/// Asset transaction errors. +enum Error { + /// Failed to match fungible. + FailedToMatchFungible, + /// `MultiLocation` to `AccountId` Conversion failed. + AccountIdConversionFailed, + /// `CurrencyId` conversion failed. + CurrencyIdConversionFailed, +} + +impl From for XcmError { + fn from(e: Error) -> Self { + match e { + Error::FailedToMatchFungible => { + XcmError::FailedToTransactAsset("FailedToMatchFungible") + } + Error::AccountIdConversionFailed => { + XcmError::FailedToTransactAsset("AccountIdConversionFailed") + } + Error::CurrencyIdConversionFailed => { + XcmError::FailedToTransactAsset("CurrencyIdConversionFailed") + } + } + } +} + +/// The handler for processing cross-chain messages +pub struct MultiCurrencyAdapter< + NativeCurrency, + TokenFactory, + Matcher, + AccountIdConverter, + AccountId, + CurrencyIdConverter, + CurrencyId, +>( + PhantomData<( + NativeCurrency, + TokenFactory, + Matcher, + AccountIdConverter, + AccountId, + CurrencyIdConverter, + CurrencyId, + )>, +); + +impl< + NativeCurrency: Currency, + TokenFactory: token_factory::TokenMinter, + Matcher: MatchesFungible, + AccountIdConverter: LocationConversion, + AccountId: sp_std::fmt::Debug + Clone, + CurrencyIdConverter: CurrencyIdConversion, + > TransactAsset + for MultiCurrencyAdapter< + NativeCurrency, + TokenFactory, + Matcher, + AccountIdConverter, + AccountId, + CurrencyIdConverter, + CurrencyId, + > +{ + fn deposit_asset(asset: &MultiAsset, location: &MultiLocation) -> XcmResult { + let who = AccountIdConverter::from_location(location) + .ok_or_else::(|| Error::AccountIdConversionFailed.into())?; + let currency = CurrencyIdConverter::from_asset(asset) + .ok_or_else::(|| Error::CurrencyIdConversionFailed.into())?; + let amount: NativeCurrency::Balance = Matcher::matches_fungible(&asset) + .ok_or_else::(|| Error::FailedToMatchFungible.into())?; + if let CurrencyId::Token(token_id) = currency { + // mint erc20 token to `who` (in the EVM) + TokenFactory::mint(token_id, who.clone(), amount) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + } else { + // native currency transfer via `frame/pallet_balances` + NativeCurrency::deposit_creating(&who, amount); + } + Ok(()) + } + + fn withdraw_asset( + asset: &MultiAsset, + location: &MultiLocation, + ) -> result::Result { + let who = AccountIdConverter::from_location(location) + .ok_or_else::(|| Error::AccountIdConversionFailed.into())?; + let currency = CurrencyIdConverter::from_asset(asset) + .ok_or_else::(|| Error::CurrencyIdConversionFailed.into())?; + let amount: NativeCurrency::Balance = Matcher::matches_fungible(&asset) + .ok_or_else::(|| Error::FailedToMatchFungible.into())?; + // match on currency variant + if let CurrencyId::Token(token_id) = currency { + // burn erc20 token from `who` + TokenFactory::burn(token_id, who.clone(), amount) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + } else { + // native currency transfer via `frame/pallet_balances` + NativeCurrency::withdraw( + &who, + amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath, + ) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + } + Ok(asset.clone()) + } +} diff --git a/pallets/xtransfer/src/lib.rs b/pallets/xtransfer/src/lib.rs new file mode 100644 index 0000000000..9f39ce4857 --- /dev/null +++ b/pallets/xtransfer/src/lib.rs @@ -0,0 +1,429 @@ +// Copyright 2019-2020 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Xtransfer handles cross-chain transfers. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod adapter; +pub mod support; + +use frame_support::pallet; + +pub use pallet::*; + +#[pallet] +pub mod pallet { + use cumulus_primitives_core::{relay_chain::Balance as RelayChainBalance, ParaId}; + use frame_support::{pallet_prelude::*, traits::Get, transactional}; + use frame_system::pallet_prelude::*; + use sp_runtime::traits::{AtLeast32BitUnsigned, Convert}; + use sp_std::{convert::Into, prelude::*}; + use token_factory::CurrencyId; + use xcm::v0::{Junction, MultiAsset, MultiLocation, NetworkId, Order, Xcm}; + + #[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, RuntimeDebug)] + /// Identity of chain. + pub enum ChainId { + /// The relay chain. + RelayChain, + /// A parachain. + ParaChain(ParaId), + } + + #[derive(Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug)] + /// Identity of cross chain currency. + pub struct XCurrencyId { + /// The reserve chain of the currency. For instance, the reserve chain + /// of DOT is Polkadot. + pub chain_id: ChainId, + /// The identity of the currency. + pub currency_id: Vec, + } + + impl Into for XCurrencyId { + fn into(self) -> MultiLocation { + MultiLocation::X1(Junction::GeneralKey(self.currency_id)) + } + } + + /// Pallet for executing cross-chain transfers + #[pallet::pallet] + pub struct Pallet(PhantomData); + + /// The shape of AccountId for (most) substrate chains (not Moonbeam, which is H160 => 20 bytes) + type AccountId32 = [u8; 32]; + + /// Configuration trait of this pallet. + #[pallet::config] + pub trait Config: frame_system::Config + cumulus_pallet_xcm_handler::Config { + /// Overarching event type + type Event: From> + IsType<::Event>; + /// Balances type + type Balance: Parameter + + Member + + AtLeast32BitUnsigned + + Default + + Copy + + MaybeSerializeDeserialize + + Into; + /// Convert local balance into relay chain balance type + type ToRelayChainBalance: Convert; + /// Convert system::AccountId to shape of inner field for Junction::AccountKey20 [u8; 20] + type AccountKey20Convert: Convert; + /// Relay chain identifier + type RelayChainNetworkId: Get; + /// This parachain's identifier + type SelfParaId: Get; + } + + #[pallet::event] + #[pallet::generate_deposit(fn deposit_event)] + pub enum Event { + /// Transferred to relay chain. \[src, dest, amount\] + TransferredToRelayChain(T::AccountId, AccountId32, T::Balance), + /// Transferred to parachain. \[x_currency_id, src, para_id, dest, dest_network, amount\] + TransferredToAccountId32Parachain( + XCurrencyId, + T::AccountId, + ParaId, + AccountId32, + NetworkId, + T::Balance, + ), + /// Transferred to parachain. \[x_currency_id, src, para_id, dest, dest_network, amount\] + TransferredToAccountKey20Parachain( + XCurrencyId, + T::AccountId, + ParaId, + T::AccountId, + NetworkId, + T::Balance, + ), + } + + #[pallet::error] + pub enum Error { + /// Cannot send message from parachain to self + CannotSendToSelf, + /// Call to SendXcm failed + FailedToSendXcm, + } + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { + /// Transfer relay chain tokens to relay chain. + /// TODO: check that relay chain token type is registered locally beforehand + #[pallet::weight(10)] + #[transactional] + pub fn transfer_to_relay_chain( + origin: OriginFor, + dest: AccountId32, + amount: T::Balance, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + let xcm = Xcm::WithdrawAsset { + assets: vec![MultiAsset::ConcreteFungible { + id: MultiLocation::X1(Junction::Parent), + amount: T::ToRelayChainBalance::convert(amount), + }], + effects: vec![Order::InitiateReserveWithdraw { + assets: vec![MultiAsset::All], + reserve: MultiLocation::X1(Junction::Parent), + effects: vec![Order::DepositAsset { + assets: vec![MultiAsset::All], + dest: MultiLocation::X1(Junction::AccountId32 { + network: T::RelayChainNetworkId::get(), + id: dest.clone(), + }), + }], + }], + }; + // TODO: revert state on xcm execution failure. + >::execute_xcm(who.clone(), xcm)?; + Self::deposit_event(Event::TransferredToRelayChain(who, dest, amount)); + Ok(().into()) + } + /// Transfer tokens to parachain that uses [u8; 32] for system::AccountId + /// TODO: check if channel exists before making any changes + #[pallet::weight(10)] + #[transactional] + pub fn transfer_to_account32_parachain( + origin: OriginFor, + x_currency_id: XCurrencyId, + para_id: ParaId, + dest: AccountId32, + dest_network: NetworkId, + amount: T::Balance, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!( + para_id != T::SelfParaId::get(), + Error::::CannotSendToSelf + ); + + let destination = Self::account_id_32_destination(dest_network.clone(), &dest); + + let xcm = match x_currency_id.chain_id { + ChainId::RelayChain => { + Self::transfer_relay_chain_tokens_to_parachain(para_id, destination, amount) + } + ChainId::ParaChain(reserve_chain) => { + if T::SelfParaId::get() == reserve_chain { + Self::transfer_owned_tokens_to_parachain( + x_currency_id.clone(), + para_id, + destination, + amount, + ) + } else { + Self::transfer_non_owned_tokens_to_parachain( + reserve_chain, + x_currency_id.clone(), + para_id, + destination, + amount, + ) + } + } + }; + // TODO: revert state on xcm execution failure. + >::execute_xcm(who.clone(), xcm)?; + Self::deposit_event(Event::TransferredToAccountId32Parachain( + x_currency_id, + who, + para_id, + dest, + dest_network, + amount, + )); + Ok(().into()) + } + /// Transfer native tokens to other parachain that uses [u8; 20] + /// TODO: check if channel exists before making any changes + #[pallet::weight(10)] + #[transactional] + pub fn transfer_native_to_account20_parachain( + origin: OriginFor, + para_id: ParaId, + dest: T::AccountId, + amount: T::Balance, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let self_id = T::SelfParaId::get(); + // TODO: ensure channel exists + ensure!(para_id != self_id, Error::::CannotSendToSelf); + let x_currency_id = XCurrencyId { + chain_id: ChainId::ParaChain(self_id), + currency_id: CurrencyId::Native.into(), + }; + let dest_network = T::RelayChainNetworkId::get(); + let destination = Self::account_key_20_destination(dest_network.clone(), dest.clone()); + let xcm = Self::transfer_owned_tokens_to_parachain( + x_currency_id.clone(), + para_id, + destination, + amount, + ); + // TODO: revert state on xcm execution failure. + >::execute_xcm(who.clone(), xcm)?; + Self::deposit_event(Event::TransferredToAccountKey20Parachain( + x_currency_id, + who, + para_id, + dest, + dest_network, + amount, + )); + Ok(().into()) + } + /// Transfer tokens to parachain that uses [u8; 20] for system::AccountId + /// TODO: check if channel exists before making any changes + #[pallet::weight(10)] + #[transactional] + pub fn transfer_to_account20_parachain( + origin: OriginFor, + x_currency_id: XCurrencyId, + para_id: ParaId, + dest: T::AccountId, + dest_network: NetworkId, + amount: T::Balance, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + ensure!( + para_id != T::SelfParaId::get(), + Error::::CannotSendToSelf + ); + + let destination = Self::account_key_20_destination(dest_network.clone(), dest.clone()); + + let xcm = match x_currency_id.chain_id { + ChainId::RelayChain => { + Self::transfer_relay_chain_tokens_to_parachain(para_id, destination, amount) + } + ChainId::ParaChain(reserve_chain) => { + if T::SelfParaId::get() == reserve_chain { + Self::transfer_owned_tokens_to_parachain( + x_currency_id.clone(), + para_id, + destination, + amount, + ) + } else { + Self::transfer_non_owned_tokens_to_parachain( + reserve_chain, + x_currency_id.clone(), + para_id, + destination, + amount, + ) + } + } + }; + // TODO: revert state on xcm execution failure. + >::execute_xcm(who.clone(), xcm)?; + Self::deposit_event(Event::TransferredToAccountKey20Parachain( + x_currency_id, + who, + para_id, + dest, + dest_network, + amount, + )); + Ok(().into()) + } + } + + impl Pallet { + /// Form multilocation when recipient chain uses AccountId32 as system::AccountId type + fn account_id_32_destination(network: NetworkId, id: &AccountId32) -> MultiLocation { + MultiLocation::X1(Junction::AccountId32 { + network, + id: id.clone(), + }) + } + /// Form multilocation when recipient chain uses AccountKey20 as system::AccountId type + fn account_key_20_destination(network: NetworkId, key: T::AccountId) -> MultiLocation { + MultiLocation::X1(Junction::AccountKey20 { + network, + key: T::AccountKey20Convert::convert(key).clone(), + }) + } + /// Returns upward message to transfer tokens from relay chain to parachain + fn transfer_relay_chain_tokens_to_parachain( + para_id: ParaId, + destination: MultiLocation, + amount: T::Balance, + ) -> Xcm { + Xcm::WithdrawAsset { + assets: vec![MultiAsset::ConcreteFungible { + id: MultiLocation::X1(Junction::Parent), + amount: T::ToRelayChainBalance::convert(amount), + }], + effects: vec![Order::InitiateReserveWithdraw { + assets: vec![MultiAsset::All], + reserve: MultiLocation::X1(Junction::Parent), + effects: vec![Order::DepositReserveAsset { + assets: vec![MultiAsset::All], + // `dest` is children parachain(of parent). + dest: MultiLocation::X1(Junction::Parachain { id: para_id.into() }), + effects: vec![Order::DepositAsset { + assets: vec![MultiAsset::All], + dest: destination, + }], + }], + }], + } + } + /// Transfer parachain tokens "owned" by self parachain to another + /// parachain. + /// + /// NOTE - `para_id` must not be self parachain. + fn transfer_owned_tokens_to_parachain( + x_currency_id: XCurrencyId, + para_id: ParaId, + destination: MultiLocation, + amount: T::Balance, + ) -> Xcm { + Xcm::WithdrawAsset { + assets: vec![MultiAsset::ConcreteFungible { + id: x_currency_id.into(), + amount: amount.into(), + }], + effects: vec![Order::DepositReserveAsset { + assets: vec![MultiAsset::All], + dest: MultiLocation::X2( + Junction::Parent, + Junction::Parachain { id: para_id.into() }, + ), + effects: vec![Order::DepositAsset { + assets: vec![MultiAsset::All], + dest: destination, + }], + }], + } + } + /// Transfer parachain tokens not "owned" by self chain to another + /// parachain. + fn transfer_non_owned_tokens_to_parachain( + reserve_chain: ParaId, + x_currency_id: XCurrencyId, + para_id: ParaId, + destination: MultiLocation, + amount: T::Balance, + ) -> Xcm { + let deposit_to_dest = Order::DepositAsset { + assets: vec![MultiAsset::All], + dest: destination, + }; + // If transfer to reserve chain, deposit to `dest` on reserve chain, + // else deposit reserve asset. + let reserve_chain_order = if para_id == reserve_chain { + deposit_to_dest + } else { + Order::DepositReserveAsset { + assets: vec![MultiAsset::All], + dest: MultiLocation::X2( + Junction::Parent, + Junction::Parachain { id: para_id.into() }, + ), + effects: vec![deposit_to_dest], + } + }; + + Xcm::WithdrawAsset { + assets: vec![MultiAsset::ConcreteFungible { + id: x_currency_id.into(), + amount: amount.into(), + }], + effects: vec![Order::InitiateReserveWithdraw { + assets: vec![MultiAsset::All], + reserve: MultiLocation::X2( + Junction::Parent, + Junction::Parachain { + id: reserve_chain.into(), + }, + ), + effects: vec![reserve_chain_order], + }], + } + } + } +} diff --git a/pallets/xtransfer/src/support.rs b/pallets/xtransfer/src/support.rs new file mode 100644 index 0000000000..124b339b90 --- /dev/null +++ b/pallets/xtransfer/src/support.rs @@ -0,0 +1,88 @@ +// Copyright 2019-2020 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM objects and relationships + +use frame_support::traits::Get; +use sp_runtime::traits::{CheckedConversion, Convert}; +use sp_std::{ + convert::{TryFrom, TryInto}, + marker::PhantomData, + prelude::*, +}; +use xcm::v0::{Junction, MultiAsset, MultiLocation}; +use xcm_executor::traits::MatchesFungible; + +// /// The XCM handler to execute XCM locally. +// pub trait XcmHandler { +// fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult; +// } + +pub trait CurrencyIdConversion { + fn from_asset(asset: &MultiAsset) -> Option; +} + +/// Matcher associated type for MultiCurrencyAdapter to convert assets into local types +pub struct IsConcreteWithGeneralKey( + PhantomData<(CurrencyId, FromRelayChainBalance)>, +); +impl MatchesFungible + for IsConcreteWithGeneralKey +where + CurrencyId: TryFrom>, + B: TryFrom, + FromRelayChainBalance: Convert, +{ + fn matches_fungible(a: &MultiAsset) -> Option { + if let MultiAsset::ConcreteFungible { id, amount } = a { + if id == &MultiLocation::X1(Junction::Parent) { + // Convert relay chain decimals to local chain + let local_amount = FromRelayChainBalance::convert(*amount); + return CheckedConversion::checked_from(local_amount); + } + if let Some(Junction::GeneralKey(key)) = id.last() { + if TryInto::::try_into(key.clone()).is_ok() { + return CheckedConversion::checked_from(*amount); + } + } + } + None + } +} + +/// Converter from MultiAsset to local Currency type +pub struct CurrencyIdConverter( + PhantomData, + PhantomData, +); +impl CurrencyIdConversion + for CurrencyIdConverter +where + CurrencyId: TryFrom>, + RelayChainCurrencyId: Get, +{ + fn from_asset(asset: &MultiAsset) -> Option { + if let MultiAsset::ConcreteFungible { id: location, .. } = asset { + if location == &MultiLocation::X1(Junction::Parent) { + return Some(RelayChainCurrencyId::get()); + } + if let Some(Junction::GeneralKey(key)) = location.last() { + return CurrencyId::try_from(key.clone()).ok(); + } + } + None + } +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index e277903bbb..4c610393c1 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -20,6 +20,8 @@ pallet-ethereum-chain-id = { path = "../pallets/ethereum-chain-id", default-feat parachain-staking = { path = "../pallets/parachain-staking", default-features = false } author-inherent = { path = "../pallets/author-inherent", default-features = false } pallet-author-filter = { path = "../pallets/author-filter", default-features = false } +token-factory = { path = "../pallets/token-factory", default-features = false } +xtransfer = { path = "../pallets/xtransfer", default-features = false } # Substrate dependencies sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "rococo-v1" } @@ -62,6 +64,11 @@ moonbeam-rpc-primitives-txpool = { path = "../primitives/rpc/txpool", default-fe # Cumulus dependencies cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "rococo-v1" } cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "rococo-v1" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "rococo-v1" } +cumulus-pallet-xcm-handler = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "rococo-v1" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "rococo-v1" } +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "rococo-v1" } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "rococo-v1" } parachain-info = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "rococo-v1" } [build-dependencies] @@ -117,6 +124,13 @@ std = [ "account/std", "parachain-staking/std", "pallet-author-filter/std", + "xtransfer/std", + "token-factory/std", + "xcm-builder/std", + "cumulus-pallet-xcm-handler/std", + "xcm-executor/std", + "xcm/std", + "polkadot-parachain/std", ] # Will be enabled by the `wasm-builder` when building the runtime for WASM. diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index af5d74e70b..7c7502862e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -28,6 +28,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use cumulus_primitives_core::relay_chain::Balance as RelayChainBalance; use fp_rpc::TransactionStatus; use frame_support::{ construct_runtime, debug, @@ -47,12 +48,13 @@ use pallet_evm::{ use pallet_transaction_payment::CurrencyAdapter; pub use parachain_staking::{InflationInfo, Range}; use parity_scale_codec::{Decode, Encode}; +use polkadot_parachain::primitives::Sibling; use sha3::{Digest, Keccak256}; use sp_api::impl_runtime_apis; use sp_core::{u32_trait::*, OpaqueMetadata, H160, H256, U256}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, Verify}, + traits::{BlakeTwo256, Block as BlockT, Convert, IdentifyAccount, IdentityLookup, Verify}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, }; @@ -60,6 +62,15 @@ use sp_std::{convert::TryFrom, prelude::*}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use token_factory::{CurrencyId, Ticker}; +use xcm::v0::{Junction, MultiLocation, NetworkId}; +use xcm_builder::{ + AccountKey20Aliases, LocationInverter, ParentIsDefault, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountKey20AsNative, + SovereignSignedViaLocation, +}; +use xcm_executor::{traits::NativeAsset, XcmExecutor}; +use xtransfer::{adapter::*, support::*}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -438,16 +449,6 @@ impl pallet_ethereum::Config for Runtime { type BlockGasLimit = BlockGasLimit; } -impl cumulus_pallet_parachain_system::Config for Runtime { - type Event = Event; - type OnValidationData = (); - type SelfParaId = ParachainInfo; - type DownwardMessageHandlers = (); - type HrmpMessageHandlers = (); -} - -impl parachain_info::Config for Runtime {} - /// GLMR, the native token, uses 18 decimals of precision. pub const GLMR: Balance = 1_000_000_000_000_000_000; @@ -500,6 +501,120 @@ impl pallet_author_filter::Config for Runtime { type RandomnessSource = RandomnessCollectiveFlip; } +pub struct AccountToH160; +impl Convert<<::Signer as IdentifyAccount>::AccountId, H160> + for AccountToH160 +{ + fn convert(from: <::Signer as IdentifyAccount>::AccountId) -> H160 { + from + } +} + +impl token_factory::Config for Runtime { + type Event = Event; + type Balance = Balance; + type TokenId = Ticker; + type AccountToH160 = AccountToH160; +} + +parameter_types! { + pub const PolkadotNetworkId: NetworkId = NetworkId::Polkadot; + pub MoonbeamNetwork: NetworkId = NetworkId::Named("moon".into()); + pub RelayChainOrigin: Origin = cumulus_pallet_xcm_handler::Origin::Relay.into(); + pub Ancestry: MultiLocation = MultiLocation::X1(Junction::Parachain { + id: ParachainInfo::get().into(), + }); + pub const RelayChainCurrencyId: CurrencyId = CurrencyId::Token(Ticker::DOT); +} + +pub type LocationConverter = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountKey20Aliases, +); + +pub struct RelayToNative; +impl Convert for RelayToNative { + fn convert(val: u128) -> Balance { + // native is 18 + // relay is 12 + val * 1_000_000 + } +} +pub struct NativeToRelay; +impl Convert for NativeToRelay { + fn convert(val: u128) -> RelayChainBalance { + // native is 18 + // relay is 12 + val / 1_000_000 + } +} + +pub type LocalAssetTransactor = MultiCurrencyAdapter< + Balances, + TokenFactory, + IsConcreteWithGeneralKey, + LocationConverter, + AccountId, + CurrencyIdConverter, + CurrencyId, +>; + +pub type LocalOriginConverter = ( + SovereignSignedViaLocation, + RelayChainAsNative, + SiblingParachainAsNative, + SignedAccountKey20AsNative, +); + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = XcmHandler; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = NativeAsset; + type IsTeleporter = (); + type LocationInverter = LocationInverter; +} + +impl cumulus_pallet_xcm_handler::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type UpwardMessageSender = ParachainSystem; + type HrmpMessageSender = ParachainSystem; + type SendXcmOrigin = EnsureRoot; + type AccountIdConverter = LocationConverter; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type Event = Event; + type OnValidationData = (); + type SelfParaId = ParachainInfo; + type DownwardMessageHandlers = XcmHandler; + type HrmpMessageHandlers = XcmHandler; +} + +impl parachain_info::Config for Runtime {} + +pub struct AccountKey20Convert; +impl Convert<<::Signer as IdentifyAccount>::AccountId, [u8; 20]> + for AccountKey20Convert +{ + fn convert(from: <::Signer as IdentifyAccount>::AccountId) -> [u8; 20] { + from.into() + } +} + +impl xtransfer::Config for Runtime { + type Event = Event; + type Balance = Balance; + type ToRelayChainBalance = NativeToRelay; + type AccountKey20Convert = AccountKey20Convert; + type RelayChainNetworkId = PolkadotNetworkId; + type SelfParaId = ParachainInfo; +} + construct_runtime! { pub enum Runtime where Block = Block, @@ -525,6 +640,9 @@ construct_runtime! { pallet_collective::::{Pallet, Call, Event, Origin, Config}, TechComitteeCollective: pallet_collective::::{Pallet, Call, Event, Origin, Config}, + TokenFactory: token_factory::{Pallet, Call, Storage, Event}, + XcmHandler: cumulus_pallet_xcm_handler::{Pallet, Call, Event, Origin}, + Xtransfer: xtransfer::{Pallet, Call, Storage, Event}, // The order matters here. Inherents will be included in the order specified here. // Concretely we need the author inherent to come after the parachain_upgrade inherent. AuthorInherent: author_inherent::{Pallet, Call, Storage, Inherent}, diff --git a/tools/config_xcmp.json b/tools/config_xcmp.json new file mode 100644 index 0000000000..e791238adc --- /dev/null +++ b/tools/config_xcmp.json @@ -0,0 +1,63 @@ +{ + "relaychain": { + "bin": "../../polkadot/target/release/polkadot", + "chain": "rococo-local", + "nodes": [ + { + "name": "alice", + "wsPort": 36944, + "port": 36444 + }, + { + "name": "bob", + "wsPort": 36955, + "port": 36555 + }, + { + "name": "charlie", + "wsPort": 36956, + "port": 36556 + } + ] + }, + "parachains": [ + { + "bin": "../target/release/moonbeam", + "id": "200", + "rpcPort": 36846, + "wsPort": 36946, + "port": 36335, + "balance": "1000", + "chain": "local", + "flags": [ + "--no-telemetry", + "--no-prometheus", + "--author-id=6be02d1d3665660d22ff9624b7be0551ee1ac91b", + "--", + "--execution=wasm" + ] + }, + { + "bin": "../target/release/moonbeam", + "id": "201", + "rpcPort": 36847, + "wsPort": 36947, + "port": 36336, + "balance": "1000", + "chain": "local", + "flags": [ + "--no-telemetry", + "--no-prometheus", + "--author-id=6be02d1d3665660d22ff9624b7be0551ee1ac91b", + "--", + "--execution=wasm" + ] + } + ], + "simpleParachains": [], + "hrmpChannels": [], + "types": { + "Address": "MultiAddress", + "LookupSource": "MultiAddress" + } +} diff --git a/tools/moonbeam-xcmp.ts b/tools/moonbeam-xcmp.ts new file mode 100644 index 0000000000..d411410b74 --- /dev/null +++ b/tools/moonbeam-xcmp.ts @@ -0,0 +1,215 @@ +import { ApiPromise, Keyring, WsProvider } from "@polkadot/api"; +import { start } from "polkadot-launch"; +import { typesBundle } from "../moonbeam-types-bundle"; + +// constants +const GERALD = "0x6be02d1d3665660d22ff9624b7be0551ee1ac91b"; +const GERALD_PRIVKEY = "0x99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342"; +const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"; +const STAKING_AMOUNT = "1.0000 kUnit"; +const GLMR = 1_000_000_000_000_000_000n; +const MIN_GLMR_STAKING = 1000n * GLMR; +const MIN_GLMR_NOMINATOR = 5n * GLMR; +const DEFAULT_GENESIS_BALANCE = 2n ** 80n; +const DEFAULT_GENESIS_STAKING = 1_000n * GLMR; +const GENESIS_ACCOUNT_BALANCE = DEFAULT_GENESIS_BALANCE - DEFAULT_GENESIS_STAKING; + +interface HrmpChannelId { + sender: number; + recipient: number; +} + +function assert(condition: boolean, msg: string) { + if (!condition) throw new Error(msg); +} + +async function wait(duration: number) { + console.log(`Waiting ${duration / 1000} seconds`); + return new Promise((res) => { + setTimeout(res, duration); + }); +} + +async function parachain_api(PORT: number): Promise { + const url = `ws://localhost:${PORT}`; + const provider = new WsProvider(url); + const api = await ApiPromise.create({ + provider, + typesBundle: typesBundle as any, + }); + return api; +} + +async function relaychain_api(PORT: number): Promise { + const url = `ws://localhost:${PORT}`; + const provider = new WsProvider(url); + const api = await ApiPromise.create({ + provider, + types: { + Address: "MultiAddress", + LookupSource: "MultiAddress", + }, + }); + return api; +} + +async function test() { + await start("config_xcmp.json"); + console.log("moonbeam launch launched"); + const WS_PORT200 = 36946; + const WS_PORT201 = 36947; + const WS_RELAY_PORT = 36944; + // first Moonbeam Parachain with ID 200 + const moonbeam200 = await parachain_api(WS_PORT200); + // second Moonbeam Parachain with ID 201 + const moonbeam201 = await parachain_api(WS_PORT201); + // relay chain + const relayApi = await relaychain_api(WS_RELAY_PORT); + // sanity checks that genesis state for all chains meet expectations + const gerald200 = await moonbeam200.query.system.account(GERALD); + assert( + gerald200.data.free.toString() === GENESIS_ACCOUNT_BALANCE.toString(), + "wrong balance for Gerald, dif: " + + (Number(GENESIS_ACCOUNT_BALANCE) - Number(gerald200.data.free)) + ); + const gerald201 = await moonbeam201.query.system.account(GERALD); + assert( + gerald201.data.free.toString() === GENESIS_ACCOUNT_BALANCE.toString(), + "wrong balance for Gerald, dif: " + + (Number(GENESIS_ACCOUNT_BALANCE) - Number(gerald201.data.free)) + ); + const relayAlice = await relayApi.query.system.account(ALICE); + assert( + "1000000000000000000" === relayAlice.data.free.toString(), + "wrong balance for relayAlice, expected: 1000000000000000000, returned: " + + Number(relayAlice.data.free) + ); + // ensure both parachains are registered on the relay chain + const dummyDead = await relayApi.query.paras.paraLifecycles(199); + assert(dummyDead.toHuman() === null, "Expected no parachain registered found Some"); + const senderAlive = await relayApi.query.paras.paraLifecycles(200); + assert(senderAlive !== null, "Expected some parachain registered found None"); + const recipientAlive = await relayApi.query.paras.paraLifecycles(201); + assert(recipientAlive !== null, "Expected some parachain registered found None"); + console.log("++ Sanity Checks Passed for Relay Chain and Both Parachains"); + // Open channel using relay sudo as caller + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const sender: number = 200; + const recipient: number = 201; + await new Promise(async (resolve) => { + const unsub = await relayApi.tx.sudo + .sudo(relayApi.tx.parasSudoWrapper.sudoEstablishHrmpChannel(sender, recipient, 8, 1024)) + .signAndSend(alice, {}, ({ events = [], status }) => { + console.log(`Current status is ${status}`); + if (status.isInBlock) { + console.log(`Transaction included at blockHash ${status.asInBlock}`); + } else if (status.isFinalized) { + console.log(`Transaction finalized at blockHash ${status.asFinalized}`); + // Loop through Vec to display all events + events.forEach(({ phase, event: { data, method, section } }) => { + console.log(`\t' ${phase}: ${section}.${method}:: ${data}`); + }); + unsub(); + resolve(); + } + }); + }); + console.log("api call resolved"); + // Check that relay chain persists storage changes (registers the necessary channel) + const channelID: HrmpChannelId = { sender, recipient }; + // @ts-ignore + const expectedChannel = await relayApi.query.hrmp.hrmpChannels(channelID); + assert(expectedChannel.toHuman() !== null, "Channel does not exist but we expected it to exist"); + // (2) TODO: check that channel deposits are reserved from sender and recipient + // HOW LONG TO WAIT UNTIL QUEUED DOWNWARD MESSAGES ARE RECEIVED BY PARARCHAIN + // await wait(50); + // (3) TODO: check that the downward message Xcm::HrmpNewChannelOpenRequest + // { sender, max_msg_size, max_capacity } + // was sent to the recipient parachain + // const recipientChannels = await moonbeam201.query.channels.recipientChannels(); + // console.log("recipientChannels", recipientChannels, recipientChannels.toHuman()); + // assert( + // recipientChannels[0] === sender, + // "Recipient channel with sender ID not yet opened on recipient chain" + // ); + // (4) TODO: check that the downward message Xcm::HrmpChannelAccepted { recipient } + // was sent to the sender parachain + // const senderChannels = await moonbeam200.query.channels.senderChannels(); + // console.log("senderChannels", senderChannels); + // assert( + // senderChannels[0] === recipient, + // "Sender channel with recipient ID not yet opened on sender chain" + // ); + // (5) Transfer from Sender to Recipient Parachain + // transfer_native_to_account20_parachain + const senderKeyring = new Keyring({ type: "ethereum" }); + const gerald = await senderKeyring.addFromUri(GERALD_PRIVKEY, null, "ethereum"); + await new Promise(async (resolve) => { + const unsub2 = await moonbeam200.tx.xtransfer + .transferNativeToAccount20Parachain(recipient, GERALD, 100000) + .signAndSend(gerald, ({ events = [], status }) => { + console.log(`Current status is ${status.type}`); + + if (status.isFinalized) { + console.log(`Transaction finalized at blockHash ${status.asFinalized}`); + + // Loopcod through Vec to display all events + events.forEach(({ phase, event: { data, method, section } }) => { + console.log(`\t' ${phase}: ${section}.${method}:: ${data}`); + }); + + unsub2(); + resolve(); + } + }); + }); + // check to see if message is received on the recipient chain + // check to see if account balance changes on sender chain + // check to see if account balance changes on recipient chain + // (6) Test transfer in the opposite direction, register a new channel first and etc + // const sender2: number = recipient; + // const recipient2: number = sender; + // const unsub3 = await relayApi.tx.sudo + // .sudo(relayApi.tx.parasSudoWrapper.sudoEstablishHrmpChannel(sender2, recipient2, 8, 1024)) + // .signAndSend(alice, {}, (result) => { + // console.log(`Current status is ${result.status}`); + // if (result.status.isInBlock) { + // console.log(`Transaction included at blockHash ${result.status.asInBlock}`); + // } else if (result.status.isFinalized) { + // console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`); + // unsub3(); + // } + // }); + console.log("all tests passed"); +} +test(); + +// construct Transact code to request open channel from 200 -> 201 +// const rawOpenCode = relayApi.tx.hrmp.hrmpInitOpenChannel(201, 8, 1024); +// console.log(rawOpenCode.toHex()); +// const rawParaCallTest = moonbeam200.tx.parachainStaking.leaveNominators(); +// console.log(rawParaCallTest.toHex()); +// remove prefix 31c04 +// const openCode = "0x1600c90000000800000000040000"; +// // Send message from 200 to relay to request open channel from 200 -> 201 +// const keyring = new Keyring({ type: "ethereum" }); +// const gerald = await keyring.addFromUri(GERALD_PRIVKEY, null, "ethereum"); +// const unsub = await moonbeam200.tx.xtransfer +// .openChannel(201, openCode) +// .signAndSend(gerald, ({ events = [], status }) => { +// console.log(`Current status is ${status.type}`); + +// if (status.isFinalized) { +// console.log(`Transaction finalized at blockHash ${status.asFinalized}`); + +// // Loopcod through Vec to display all events +// events.forEach(({ phase, event: { data, method, section } }) => { +// console.log(`\t' ${phase}: ${section}.${method}:: ${data}`); +// }); + +// unsub(); +// } +// }); +// Send message from 201 to relay to accept channel request from 200 -> 201 +// Transfer Moonbeam from 200 to 201 diff --git a/tools/package.json b/tools/package.json index 8a3d97f5d6..69e44b7fba 100644 --- a/tools/package.json +++ b/tools/package.json @@ -28,6 +28,7 @@ "build-moonbeam-launch": "yarn add PureStake/polkadot-launch#moonbeam-launch", "moonbeam-launch": "yarn run build-moonbeam-launch && mkdir -p specFiles && ts-node moonbeam-launch.ts", "moonbeam-test": "yarn run build-moonbeam-launch && mkdir -p specFiles && ts-node moonbeam-test.ts", + "moonbeam-xcmp": "yarn run build-moonbeam-launch && mkdir -p specFiles && ts-node moonbeam-xcmp.ts", "test-staking": "yarn run build-moonbeam-launch && mkdir -p specFiles && ts-node test-staking.ts" } } diff --git a/tools/yarn.lock b/tools/yarn.lock index 8e59249e5e..19a1ee48de 100644 --- a/tools/yarn.lock +++ b/tools/yarn.lock @@ -118,7 +118,7 @@ babel-plugin-polyfill-regenerator "^0.1.2" semver "^6.3.0" -"@babel/runtime@^7.13.7", "@babel/runtime@^7.13.8", "@babel/runtime@^7.13.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.13.10", "@babel/runtime@^7.13.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.9.2": version "7.13.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== @@ -358,20 +358,6 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" -"@polkadot/api-derive@3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-3.11.1.tgz#3f0d6804ca2ade80da100314d576534490db5727" - integrity sha512-/v/fNSivgucQrDJvwLU17u8iZ0oQipQzgpofCJGQhRv8OaSv/E9g5EXcHJ1ri/Ozevgu5cPmGs96lLkQaPieAw== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/api" "3.11.1" - "@polkadot/rpc-core" "3.11.1" - "@polkadot/types" "3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/util-crypto" "^5.9.2" - "@polkadot/x-rxjs" "^5.9.2" - bn.js "^4.11.9" - "@polkadot/api-derive@4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-4.0.3.tgz#542d871d84c64f927565d655eabce0ec2b917a4a" @@ -386,24 +372,19 @@ "@polkadot/x-rxjs" "^6.0.5" bn.js "^4.11.9" -"@polkadot/api@3.11.1", "@polkadot/api@^3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-3.11.1.tgz#854ee9686942a4be736932d61bf7ddb1242a0966" - integrity sha512-VqEh2n13ESLxnTUKujUfZ3Spct+lTycNgrX+IWD7/f05GsMwhCZLYtt708K8nqGFH2OKDl8xzwuGCvRN/05U1Q== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/api-derive" "3.11.1" - "@polkadot/keyring" "^5.9.2" - "@polkadot/metadata" "3.11.1" - "@polkadot/rpc-core" "3.11.1" - "@polkadot/rpc-provider" "3.11.1" - "@polkadot/types" "3.11.1" - "@polkadot/types-known" "3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/util-crypto" "^5.9.2" - "@polkadot/x-rxjs" "^5.9.2" +"@polkadot/api-derive@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-4.4.1.tgz#2e3a26b59573a04c18e05b85e0b8a951a7f970b0" + integrity sha512-N+U4S2zT4W9UNaJpOlEn8kvUnchiqvfYXnD1CXhxbn8yLo3jMW/WNJ8KksxxW2jg3RGW24slYVARvYFHBrlFYQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@polkadot/api" "4.4.1" + "@polkadot/rpc-core" "4.4.1" + "@polkadot/types" "4.4.1" + "@polkadot/util" "^6.0.5" + "@polkadot/util-crypto" "^6.0.5" + "@polkadot/x-rxjs" "^6.0.5" bn.js "^4.11.9" - eventemitter3 "^4.0.7" "@polkadot/api@4.0.3", "@polkadot/api@^4.0.3": version "4.0.3" @@ -424,14 +405,24 @@ bn.js "^4.11.9" eventemitter3 "^4.0.7" -"@polkadot/keyring@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-5.9.2.tgz#f8011a524767bb8f000bec3e26178fc5feffae5b" - integrity sha512-h9AhrzyUmludbmo0ixRFLEyRJvUc7GTl5koSBrG0uv+9Yn0I/7YRgAKn3zKcUVZyvgoLvzZnBFwekGbdFcl9Yg== +"@polkadot/api@4.4.1", "@polkadot/api@^4.2.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-4.4.1.tgz#7f5e9bdafce53e34505875183c9cbcdd966a6e8b" + integrity sha512-r/zpFYVebp6TYE5SjEH9eYNFQEq4IzNYM6rM65d0nPCbXFVmpzY5hsqPUhiRL6+drdhEO47pgxAow+60Pdx1fg== dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/util" "5.9.2" - "@polkadot/util-crypto" "5.9.2" + "@babel/runtime" "^7.13.10" + "@polkadot/api-derive" "4.4.1" + "@polkadot/keyring" "^6.0.5" + "@polkadot/metadata" "4.4.1" + "@polkadot/rpc-core" "4.4.1" + "@polkadot/rpc-provider" "4.4.1" + "@polkadot/types" "4.4.1" + "@polkadot/types-known" "4.4.1" + "@polkadot/util" "^6.0.5" + "@polkadot/util-crypto" "^6.0.5" + "@polkadot/x-rxjs" "^6.0.5" + bn.js "^4.11.9" + eventemitter3 "^4.0.7" "@polkadot/keyring@^6.0.5": version "6.0.5" @@ -442,18 +433,6 @@ "@polkadot/util" "6.0.5" "@polkadot/util-crypto" "6.0.5" -"@polkadot/metadata@3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-3.11.1.tgz#c3e9645f6f78c8e02e0da695f3718b9d69f450a8" - integrity sha512-Z3KtOTX2kU+vvbRDiGY+qyPpF/4xTpnUipoNGijIGQ/EWWcgrm8sSgPzZQhHCfgIqM+jq3g9GvPMYeQp2Yy3ng== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/types" "3.11.1" - "@polkadot/types-known" "3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/util-crypto" "^5.9.2" - bn.js "^4.11.9" - "@polkadot/metadata@4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-4.0.3.tgz#f0c7b63f8d0f40d8f81218849613f35edcb8157b" @@ -466,12 +445,17 @@ "@polkadot/util-crypto" "^6.0.5" bn.js "^4.11.9" -"@polkadot/networks@5.9.2", "@polkadot/networks@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-5.9.2.tgz#c687525b5886c9418f75240afe22b562ed88e2dd" - integrity sha512-JQyXJDJTZKQtn8y3HBHWDhiBfijhpiXjVEhY+fKvFcQ82TaKmzhnipYX0EdBoopZbuxpn/BJy6Y1Y/3y85EC+g== +"@polkadot/metadata@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-4.4.1.tgz#aeecee93f0c63183dddd4a4f4f987cd3ebacf085" + integrity sha512-rkOaB90BkYvWLc1gKfY4/hGtXriWi/8LmopDV241on9ED5q8gvD5gX8gamrHeK7p0IDvUELkP35/0zyKpK2ihw== dependencies: - "@babel/runtime" "^7.13.8" + "@babel/runtime" "^7.13.10" + "@polkadot/types" "4.4.1" + "@polkadot/types-known" "4.4.1" + "@polkadot/util" "^6.0.5" + "@polkadot/util-crypto" "^6.0.5" + bn.js "^4.11.9" "@polkadot/networks@6.0.5", "@polkadot/networks@^6.0.5": version "6.0.5" @@ -480,18 +464,6 @@ dependencies: "@babel/runtime" "^7.13.9" -"@polkadot/rpc-core@3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-3.11.1.tgz#fc57ac6429fd0322ac8421f434868cd244ede86f" - integrity sha512-8KTEZ/c2/TrsTOrrqxxNbyjO5P/033R/yTDgwqL0gwmF+ApnH3vB65YfKqaxn+rBWOMQS0jQhF6KZdtXvRcuYg== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/metadata" "3.11.1" - "@polkadot/rpc-provider" "3.11.1" - "@polkadot/types" "3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/x-rxjs" "^5.9.2" - "@polkadot/rpc-core@4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-4.0.3.tgz#40046acd961d925ef69532694f48437c7bceef0c" @@ -504,20 +476,17 @@ "@polkadot/util" "^6.0.5" "@polkadot/x-rxjs" "^6.0.5" -"@polkadot/rpc-provider@3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-3.11.1.tgz#d4ee901f464211b3b03607615a6db208ef186213" - integrity sha512-5OKh3rAg8l10M+tGLCoxhEoH9uEtK0ehJfOHUmdtwmwIk5aBFZ/ZTeiDkPM+/l84PCzYmp2uzO+YNsyMWUoVLw== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/types" "3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/util-crypto" "^5.9.2" - "@polkadot/x-fetch" "^5.9.2" - "@polkadot/x-global" "^5.9.2" - "@polkadot/x-ws" "^5.9.2" - bn.js "^4.11.9" - eventemitter3 "^4.0.7" +"@polkadot/rpc-core@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-4.4.1.tgz#bf010efa0bafb4b353db10067b945457ba3852eb" + integrity sha512-tDN6qt0LYbslHVS4iTlssNaqv5Zepe9do0Edew6oa7b4wAr5zmXbsyhlVQFgaPvi/whNjpcyRF2ZYxhGbEkd6Q== + dependencies: + "@babel/runtime" "^7.13.10" + "@polkadot/metadata" "4.4.1" + "@polkadot/rpc-provider" "4.4.1" + "@polkadot/types" "4.4.1" + "@polkadot/util" "^6.0.5" + "@polkadot/x-rxjs" "^6.0.5" "@polkadot/rpc-provider@4.0.3": version "4.0.3" @@ -534,16 +503,20 @@ bn.js "^4.11.9" eventemitter3 "^4.0.7" -"@polkadot/types-known@3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-3.11.1.tgz#f695c9155fa54eeed95cea179bb8cb2398726bd3" - integrity sha512-ImAxyCdqblmlXaMlgvuXZ6wzZgOYgE40FgWaYRJpFXRGJLDwtcJcpVI+7m/ns5dJ3WujboEMOHVR1HPpquw8Jw== +"@polkadot/rpc-provider@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-4.4.1.tgz#c60e27253dc0be7c6361ae63e10ac77795588e74" + integrity sha512-6v4iAkFzbeCLaCBvsTVTSyCy+Fp7AzMKySpdB4g/+MG8dupqts5f2BDmlnMFHBlNBDantVpJoMTvfvLfLxJz7g== dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/networks" "^5.9.2" - "@polkadot/types" "3.11.1" - "@polkadot/util" "^5.9.2" + "@babel/runtime" "^7.13.10" + "@polkadot/types" "4.4.1" + "@polkadot/util" "^6.0.5" + "@polkadot/util-crypto" "^6.0.5" + "@polkadot/x-fetch" "^6.0.5" + "@polkadot/x-global" "^6.0.5" + "@polkadot/x-ws" "^6.0.5" bn.js "^4.11.9" + eventemitter3 "^4.0.7" "@polkadot/types-known@4.0.3": version "4.0.3" @@ -556,17 +529,15 @@ "@polkadot/util" "^6.0.5" bn.js "^4.11.9" -"@polkadot/types@3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-3.11.1.tgz#c0188390dfda84d746d57f7818ad622ac6b1de8b" - integrity sha512-+BWsmveYVkLFx/csvPmU+NhNFhf+0srAt2d0f+7y663nitc/sng1AcEDPbrbXHSQVyPdvI20Mh4Escl4aR+TLw== +"@polkadot/types-known@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-4.4.1.tgz#dfd7e9bb8cafd50fb1438270f0310fbdf78c978d" + integrity sha512-JPRqRnwxTweDZMWVg+vjFLK2xB3dsJsBUcUGoL96KH8klywk5m07JueRYdNuWhnZ77OloSo+0FvLWC83oBhBfg== dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/metadata" "3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/util-crypto" "^5.9.2" - "@polkadot/x-rxjs" "^5.9.2" - "@types/bn.js" "^4.11.6" + "@babel/runtime" "^7.13.10" + "@polkadot/networks" "^6.0.5" + "@polkadot/types" "4.4.1" + "@polkadot/util" "^6.0.5" bn.js "^4.11.9" "@polkadot/types@4.0.3": @@ -582,27 +553,18 @@ "@types/bn.js" "^4.11.6" bn.js "^4.11.9" -"@polkadot/util-crypto@5.9.2", "@polkadot/util-crypto@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-5.9.2.tgz#3858cfffe7732458b4a2b38ece01eaf52a3746c2" - integrity sha512-d8CW2grI3gWi6d/brmcZQWaMPHqQq5z7VcM74/v8D2KZ+hPYL3B0Jn8zGL1vtgMz2qdpWrZdAe89LBC8BvM9bw== +"@polkadot/types@4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-4.4.1.tgz#85bdbdd965e33e2832bf77e7ffc5741e513938f7" + integrity sha512-sP0yqAKmv4WcFYX3fGdZ2xFaLAo1dcTY9gSL4twnG1TYBh1FT9fTux1d0d46VSPM6/+fbTV/HSIBa6wSETRHUQ== dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/networks" "5.9.2" - "@polkadot/util" "5.9.2" - "@polkadot/wasm-crypto" "^3.2.4" - "@polkadot/x-randomvalues" "5.9.2" - base-x "^3.0.8" - base64-js "^1.5.1" - blakejs "^1.1.0" + "@babel/runtime" "^7.13.10" + "@polkadot/metadata" "4.4.1" + "@polkadot/util" "^6.0.5" + "@polkadot/util-crypto" "^6.0.5" + "@polkadot/x-rxjs" "^6.0.5" + "@types/bn.js" "^4.11.6" bn.js "^4.11.9" - create-hash "^1.2.0" - elliptic "^6.5.4" - hash.js "^1.1.7" - js-sha3 "^0.8.0" - scryptsy "^2.1.0" - tweetnacl "^1.0.3" - xxhashjs "^0.2.2" "@polkadot/util-crypto@6.0.5", "@polkadot/util-crypto@^6.0.5": version "6.0.5" @@ -626,19 +588,6 @@ tweetnacl "^1.0.3" xxhashjs "^0.2.2" -"@polkadot/util@5.9.2", "@polkadot/util@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-5.9.2.tgz#ad2494e78ca6c3aadd6fb394a6be55020dc9b2a8" - integrity sha512-p225NJusnXeu7i2iAb8HAGWiMOUAnRaIyblIjJ4F89ZFZZ4amyliGxe5gKcyjRgxAJ44WdKyBLl/8L3rNv8hmQ== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/x-textdecoder" "5.9.2" - "@polkadot/x-textencoder" "5.9.2" - "@types/bn.js" "^4.11.6" - bn.js "^4.11.9" - camelcase "^5.3.1" - ip-regex "^4.3.0" - "@polkadot/util@6.0.5", "@polkadot/util@^6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-6.0.5.tgz#aa52995d3fe998eed218d26b243832a7a3e2944d" @@ -652,13 +601,6 @@ camelcase "^5.3.1" ip-regex "^4.3.0" -"@polkadot/wasm-crypto-asmjs@^3.2.4": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-3.2.4.tgz#837f5b723161b21670d13779eff4c061f7947577" - integrity sha512-fgN26iL+Pbb35OYsDIRHC74Xnwde+A5u3OjEcQ9zJhM391eOTuKsQ2gyC9TLNAKqeYH8pxsa27yjRO71We7FUA== - dependencies: - "@babel/runtime" "^7.13.7" - "@polkadot/wasm-crypto-asmjs@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-4.0.2.tgz#f42c353a64e1243841daf90e4bd54eff01a4e3cf" @@ -666,13 +608,6 @@ dependencies: "@babel/runtime" "^7.13.9" -"@polkadot/wasm-crypto-wasm@^3.2.4": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-3.2.4.tgz#70885e06a813af91d81cf7e8ff826976fa99a38b" - integrity sha512-Q/3IEpoo7vkTzg40GxehRK000A9oBgjbh/uWCNQ8cMqWLYYCfzZy4NIzw8szpxNiSiGfGL0iZlP4ZSx2ZqEe2g== - dependencies: - "@babel/runtime" "^7.13.7" - "@polkadot/wasm-crypto-wasm@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-4.0.2.tgz#89f9e0a1e4d076784d4a42bea37fc8b06bdd8bb6" @@ -680,15 +615,6 @@ dependencies: "@babel/runtime" "^7.13.9" -"@polkadot/wasm-crypto@^3.2.4": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-3.2.4.tgz#c3e23ff728c1d5701215ae15ecdc605e96901989" - integrity sha512-poeRU91zzZza0ZectT63vBiAqh6DsHCyd3Ogx1U6jsYiRa0yuECMWJx1onvnseDW4tIqsC8vZ/9xHXWwhjTAVg== - dependencies: - "@babel/runtime" "^7.13.7" - "@polkadot/wasm-crypto-asmjs" "^3.2.4" - "@polkadot/wasm-crypto-wasm" "^3.2.4" - "@polkadot/wasm-crypto@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.0.2.tgz#9649057adee8383cc86433d107ba526b718c5a3b" @@ -698,16 +624,6 @@ "@polkadot/wasm-crypto-asmjs" "^4.0.2" "@polkadot/wasm-crypto-wasm" "^4.0.2" -"@polkadot/x-fetch@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-5.9.2.tgz#0ec2b00bd253b896f7e435dfba34ebac914269e1" - integrity sha512-Nx7GfyOmMdqn5EX+wf6PnIwleQX+aGqzdbYhozNLF54IoNFLHLOs6hCYnBlKbmM1WyukMZMjg2YxyZRQWcHKPQ== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/x-global" "5.9.2" - "@types/node-fetch" "^2.5.8" - node-fetch "^2.6.1" - "@polkadot/x-fetch@^6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-6.0.5.tgz#b6c90c478f9951ab1f9c835d48869c669c45120e" @@ -718,15 +634,6 @@ "@types/node-fetch" "^2.5.8" node-fetch "^2.6.1" -"@polkadot/x-global@5.9.2", "@polkadot/x-global@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-5.9.2.tgz#e223d59536d168c7cbc49fc3a2052cbd71bd7256" - integrity sha512-wpY6IAOZMGiJQa8YMm7NeTLi9bwnqqVauR+v7HwyrssnGPuYX8heb6BQLOnnnPh/EK0+M8zNtwRBU48ez0/HOg== - dependencies: - "@babel/runtime" "^7.13.8" - "@types/node-fetch" "^2.5.8" - node-fetch "^2.6.1" - "@polkadot/x-global@6.0.5", "@polkadot/x-global@^6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-6.0.5.tgz#eb2a0980e4c2012f251e7b61832e185f5037ae80" @@ -736,14 +643,6 @@ "@types/node-fetch" "^2.5.8" node-fetch "^2.6.1" -"@polkadot/x-randomvalues@5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-5.9.2.tgz#563a76550f94107ce5a37c462ed067dc040626b1" - integrity sha512-Zv+eXSP3oBImMnB82y05Doo0A96WUFsQDbnLHI3jFHioIg848cL0nndB9TgBwPaFkZ2oiwoHEC8yxqNI6/jkzQ== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/x-global" "5.9.2" - "@polkadot/x-randomvalues@6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-6.0.5.tgz#32aa5e670acf3ab13af281f9c0871c279de24b0a" @@ -752,14 +651,6 @@ "@babel/runtime" "^7.13.9" "@polkadot/x-global" "6.0.5" -"@polkadot/x-rxjs@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-5.9.2.tgz#925b7c3325678b137ca30af6a726b22c5e8f9125" - integrity sha512-cuF4schclspOfAqEPvbcA3aQ9d3TBy2ORZ8YehxD0ZSHWJNhefHDIUDgS5T3NtPhSKgcEmSlI5TfVfgGFxgVMg== - dependencies: - "@babel/runtime" "^7.13.8" - rxjs "^6.6.6" - "@polkadot/x-rxjs@^6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-6.0.5.tgz#829b12f225a4252ae16e405fe7876cce390eabd6" @@ -768,14 +659,6 @@ "@babel/runtime" "^7.13.9" rxjs "^6.6.6" -"@polkadot/x-textdecoder@5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-5.9.2.tgz#2e69922acc426f91adc2629fea362e41c9035f25" - integrity sha512-MCkgITwGY3tG0UleDkBJEoiKGk/YWYwMM5OR6fNo07RymHRtJ8OLJC+Sej9QD05yz6TIhFaaRRYzmtungIcwTw== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/x-global" "5.9.2" - "@polkadot/x-textdecoder@6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-6.0.5.tgz#919a8991c9e81610a3c4f6bf314331071f2f8345" @@ -784,14 +667,6 @@ "@babel/runtime" "^7.13.9" "@polkadot/x-global" "6.0.5" -"@polkadot/x-textencoder@5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-5.9.2.tgz#67362e64bacfe6ff4eec73bf596873a2f9d9f36d" - integrity sha512-IjdLY3xy0nUfps1Bdi0tRxAX7X081YyoiSWExwqUkChdcYGMqMe3T2wqrrt9qBr2IkW8O/tlfYBiZXdII0YCcw== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/x-global" "5.9.2" - "@polkadot/x-textencoder@6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-6.0.5.tgz#fc851259de97a98f3417e51807c1f5ebe265fdf0" @@ -800,16 +675,6 @@ "@babel/runtime" "^7.13.9" "@polkadot/x-global" "6.0.5" -"@polkadot/x-ws@^5.9.2": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-5.9.2.tgz#294f226be5ef07363426b8cf2729cba12d9637e5" - integrity sha512-6A/cteC0B3hm64/xG6DNG8qGsHAXJgAy9wjcB38qnoJGYl12hysIFjPeHD+V0W/LOl9payW6kpZzhisLlVOZpQ== - dependencies: - "@babel/runtime" "^7.13.8" - "@polkadot/x-global" "5.9.2" - "@types/websocket" "^1.0.1" - websocket "^1.0.33" - "@polkadot/x-ws@^6.0.5": version "6.0.5" resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-6.0.5.tgz#bafab6004d88d9273478332a3a040bfef3647619" @@ -898,7 +763,7 @@ dependencies: "@types/node" "*" -"@types/websocket@^1.0.1", "@types/websocket@^1.0.2": +"@types/websocket@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.2.tgz#d2855c6a312b7da73ed16ba6781815bf30c6187a" integrity sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ== @@ -4112,11 +3977,11 @@ pkg@^4.4.9: polkadot-launch@PureStake/polkadot-launch#moonbeam-launch: version "1.0.12" - resolved "https://codeload.github.com/PureStake/polkadot-launch/tar.gz/077ecbe74d0ebc30d337ce98429e1306d87ac04c" + resolved "https://codeload.github.com/PureStake/polkadot-launch/tar.gz/c4eea0a021cd75afc9f3bbc9e4a580e473e5e3ed" dependencies: - "@polkadot/api" "^3.11.1" - "@polkadot/util" "^5.9.2" - "@polkadot/util-crypto" "^5.9.2" + "@polkadot/api" "^4.2.1" + "@polkadot/util" "^6.0.5" + "@polkadot/util-crypto" "^6.0.5" "@types/chai" "^4.2.14" "@types/mocha" "^8.2.0" chai "^4.2.0"