├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github ├── ISSUE_TEMPLATE │ ├── ask-a-question.md │ ├── report-a-bug.md │ └── suggest-a-feature.md └── workflows │ └── build-push-template.yml ├── .gitignore ├── .vscode └── tasks.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── docker-compose.yml ├── node ├── Cargo.toml ├── build.rs └── src │ ├── chain_spec.rs │ ├── cli.rs │ ├── command.rs │ ├── lib.rs │ ├── main.rs │ ├── rpc.rs │ └── service.rs ├── pallets └── exchange │ ├── Cargo.toml │ └── src │ ├── benchmarking.rs │ ├── lib.rs │ ├── mock.rs │ └── tests.rs ├── runtime ├── Cargo.toml ├── build.rs └── src │ ├── lib.rs │ └── multiaddress.rs ├── scripts ├── docker_run.sh └── init.sh └── types.json /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM paritytech/substrate-playground-template-base:sha-0793587 2 | 3 | # Here the whole repo is already accessible at . (thanks to the inherited image) 4 | 5 | RUN cargo build --release 6 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Substrate Node template", 3 | "dockerFile": "Dockerfile", 4 | "settings": { 5 | "terminal.integrated.shell.linux": "/bin/bash", 6 | "lldb.executable": "/usr/bin/lldb" 7 | }, 8 | "extensions": [ 9 | "rust-lang.rust", 10 | "bungcip.better-toml", 11 | "vadimcn.vscode-lldb" 12 | ], 13 | "forwardPorts": [ 14 | 3000, 15 | 9944 16 | ], 17 | "image": "paritytech/substrate-playground-template-node-template:sha-c9fda53" 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ask-a-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask a Question 3 | about: Ask a question about this template. 4 | title: "" 5 | labels: question 6 | assignees: "" 7 | --- 8 | 9 | **Question** 10 | 11 | _Please include information such as the following: is your question to clarify an existing resource 12 | or are you asking about something new? what are you trying to accomplish? where have you looked for 13 | answers?_ 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report-a-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a Bug 3 | about: Report a problem with this template. 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Description** 10 | 11 | _Tell us what happened. In particular, be specific about any changes you made to this template. 12 | Ideally, provide a link to your project's GitHub repository. Please note that we are not able to 13 | support all conceivable changes to this template project, but the more information you are able to 14 | provide the more equipped we will be to help._ 15 | 16 | **Steps to Reproduce** 17 | 18 | _Replace the example steps below with actual steps to reproduce the bug you're reporting._ 19 | 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected vs. Actual Behavior** 26 | 27 | _What did you expect to happen after you followed the steps you described in the last section? What 28 | actually happened?_ 29 | 30 | **Environment** 31 | 32 | _Describe the environment in which you encountered this bug. Use the list below as a starting point 33 | and add additional information if you think it's relevant._ 34 | 35 | - Operating system: 36 | - Template version/tag: 37 | - Rust version (run `rustup show`): 38 | 39 | **Logs, Errors or Screenshots** 40 | 41 | _Please provide the text of any logs or errors that you experienced; if 42 | applicable, provide screenshots to help illustrate the problem._ 43 | 44 | **Additional Information** 45 | 46 | _Please add any other details that you think may help us solve your problem._ 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/suggest-a-feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Suggest a Feature 3 | about: Suggest a new feature or an improvement to an existing feature for this template. 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **Motivation** 10 | 11 | _Describe the need or frustration that motivated you to make this suggestion. Please note that the 12 | goal of this project is to provide a general-purpose template project, so please take care when 13 | suggesting features that may be specific to a particular use case._ 14 | 15 | **Suggested Solution** 16 | 17 | _Describe your suggested solution to the need or frustration that you are experiencing._ 18 | 19 | **Alternatives** 20 | 21 | _Describe any alternative solutions or features you considered and why you believe your suggested 22 | solution is preferable._ 23 | 24 | **Additional Information** 25 | 26 | _Provide any additional information that you believe may help us evaluate your suggestion._ 27 | -------------------------------------------------------------------------------- /.github/workflows/build-push-template.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push template 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build-push-template: 10 | if: ${{ github.repository == 'substrate-developer-hub/substrate-node-template' }} 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - name: Trigger playground inclusion 14 | uses: peter-evans/repository-dispatch@v1 15 | with: 16 | token: ${{ secrets.REPO_ACCESS_TOKEN }} 17 | repository: paritytech/substrate-playground 18 | event-type: template-updated 19 | client-payload: '{"id": "node-template"}' 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | # These are backup files generated by rustfmt 5 | **/*.rs.bk 6 | 7 | .DS_Store 8 | 9 | # The cache for docker container dependency 10 | .cargo 11 | 12 | # The cache for chain data in container 13 | .local -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Run ", 8 | "type": "shell", 9 | "command": "cargo", 10 | "args": ["run", "--release", "--", "--dev"], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | }, 15 | "presentation": { 16 | "reveal": "always", 17 | "panel": "new" 18 | }, 19 | "problemMatcher": [ 20 | { 21 | "owner": "rust", 22 | "fileLocation": ["relative", "${workspaceRoot}"], 23 | "pattern": { 24 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(\\d+):(\\d+)\\s+(warning|error):\\s+(.*)$", 25 | "file": 1, 26 | "line": 2, 27 | "column": 3, 28 | "endLine": 4, 29 | "endColumn": 5, 30 | "severity": 6, 31 | "message": 7 32 | } 33 | } 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.release] 2 | panic = 'unwind' 3 | 4 | [workspace] 5 | members = [ 6 | 'node', 7 | 'pallets/exchange', 8 | 'runtime', 9 | ] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Runtime Module Library (ORML) Workshop 2 | 3 | Sub0 Workshop Link: https://www.crowdcast.io/e/axvfinsv/19 4 | 5 | This is a workshop project for learning about blockchain runtime development with 6 | [Substrate](https://substrate.dev/), 7 | [FRAME](https://substrate.dev/docs/en/knowledgebase/runtime/frame) and the 8 | [Open Runtime Module Library](https://github.com/open-web3-stack/open-runtime-module-library). This 9 | project implements a simple exchange protocol that is built on top of the ORML 10 | [Currencies](https://github.com/open-web3-stack/open-runtime-module-library/tree/master/currencies) 11 | and [Tokens](https://github.com/open-web3-stack/open-runtime-module-library/tree/master/tokens) 12 | pallets. After completing this workshop, participants should have a better understanding of how to 13 | design and implement a FRAME pallet. 14 | 15 | It is recommanded to read commit by commit to understand each individual steps. 16 | 17 | ## Add ORML Pallets 18 | 19 | Find the implementations for the Currencies and Tokens pallets in 20 | [the runtime](blob/master/runtime/src/lib.rs). Notice that the Tokens pallet is configured with a 21 | `CurrencyId` set that specifies a native token; the Currencies pallet is configured to depend on the 22 | Tokens pallet. 23 | 24 | ## Define an Exchange Protocol 25 | 26 | The Exchange pallet defines a simple interface that depends on the ORML pallets that were configured 27 | in the previous step: 28 | 29 | - `submit_order(from_id, from_amt, to_id, to_amt)` 30 | - `take_order(order_id)` 31 | - `cancel_order(order_id)` 32 | 33 | ## Build & Run 34 | 35 | If you need to, 36 | [set up your Substrate development environment](https://substrate.dev/docs/en/knowledgebase/getting-started/#manual-installation). 37 | Then, build and run a development chain: 38 | 39 | ```shell 40 | $ cargo run -- --dev --tmp 41 | ``` 42 | 43 | Once the node is running, use this link to open the Polkadot JS Apps UI and connect to the Substrate 44 | node: https://polkadot.js.org/apps/#/settings/developer?rpc=ws://127.0.0.1:9944. Use the Settings > 45 | Developer app and the contents of the [`types.json`](blob/master/types.json) file to add the 46 | necessary types to the UI. 47 | 48 | ## Upstream 49 | 50 | This project was forked from the 51 | [Substrate Developer Hub Node Template](https://github.com/substrate-developer-hub/substrate-node-template). 52 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.2" 2 | 3 | services: 4 | dev: 5 | container_name: node-template 6 | image: paritytech/ci-linux:production 7 | working_dir: /var/www/node-template 8 | ports: 9 | - "9944:9944" 10 | environment: 11 | - CARGO_HOME=/var/www/node-template/.cargo 12 | volumes: 13 | - .:/var/www/node-template 14 | - type: bind 15 | source: ./.local 16 | target: /root/.local 17 | command: bash -c "cargo build --release && ./target/release/node-template --dev --ws-external" 18 | -------------------------------------------------------------------------------- /node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Substrate DevHub '] 3 | build = 'build.rs' 4 | description = 'A fresh FRAME-based Substrate node, ready for hacking.' 5 | edition = '2018' 6 | homepage = 'https://substrate.dev' 7 | license = 'Unlicense' 8 | name = 'node-template' 9 | repository = 'https://github.com/substrate-developer-hub/substrate-node-template/' 10 | version = '3.0.0' 11 | 12 | [[bin]] 13 | name = 'node-template' 14 | 15 | [package.metadata.docs.rs] 16 | targets = ['x86_64-unknown-linux-gnu'] 17 | 18 | [build-dependencies] 19 | substrate-build-script-utils = '3.0.0' 20 | 21 | [dependencies] 22 | jsonrpc-core = '15.1.0' 23 | structopt = '0.3.8' 24 | serde_json = "1.0.41" 25 | 26 | # local dependencies 27 | node-template-runtime = { path = '../runtime', version = '3.0.0' } 28 | 29 | # Substrate dependencies 30 | frame-benchmarking = '3.1.0' 31 | frame-benchmarking-cli = '3.0.0' 32 | pallet-transaction-payment-rpc = '3.0.0' 33 | sc-basic-authorship = '0.9.0' 34 | sc-cli = { features = ['wasmtime'], version = '0.9.0' } 35 | sc-client-api = '3.0.0' 36 | sc-consensus = '0.9.0' 37 | sc-consensus-aura = '0.9.0' 38 | sc-executor = { features = ['wasmtime'], version = '0.9.0' } 39 | sc-finality-grandpa = '0.9.0' 40 | sc-keystore = '3.0.0' 41 | sc-rpc = '3.0.0' 42 | sc-rpc-api = '0.9.0' 43 | sc-service = { features = ['wasmtime'], version = '0.9.0' } 44 | sc-telemetry = '3.0.0' 45 | sc-transaction-pool = '3.0.0' 46 | sp-api = '3.0.0' 47 | sp-block-builder = '3.0.0' 48 | sp-blockchain = '3.0.0' 49 | sp-consensus = '0.9.0' 50 | sp-consensus-aura = '0.9.0' 51 | sp-core = '3.0.0' 52 | sp-finality-grandpa = '3.0.0' 53 | sp-inherents = '3.0.0' 54 | sp-runtime = '3.0.0' 55 | sp-transaction-pool = '3.0.0' 56 | substrate-frame-rpc-system = '3.0.0' 57 | 58 | [features] 59 | default = [] 60 | runtime-benchmarks = ['node-template-runtime/runtime-benchmarks'] 61 | -------------------------------------------------------------------------------- /node/build.rs: -------------------------------------------------------------------------------- 1 | use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; 2 | 3 | fn main() { 4 | generate_cargo_keys(); 5 | 6 | rerun_if_git_head_changed(); 7 | } 8 | -------------------------------------------------------------------------------- /node/src/chain_spec.rs: -------------------------------------------------------------------------------- 1 | use sp_core::{Pair, Public, sr25519}; 2 | use node_template_runtime::{ 3 | AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, 4 | SudoConfig, SystemConfig, WASM_BINARY, Signature, TokensConfig, CurrencyId, 5 | }; 6 | use sp_consensus_aura::sr25519::AuthorityId as AuraId; 7 | use sp_finality_grandpa::AuthorityId as GrandpaId; 8 | use sp_runtime::traits::{Verify, IdentifyAccount}; 9 | use sc_service::ChainType; 10 | use serde_json::map::Map; 11 | 12 | // The URL for the telemetry server. 13 | // const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; 14 | 15 | /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. 16 | pub type ChainSpec = sc_service::GenericChainSpec; 17 | 18 | /// Generate a crypto pair from seed. 19 | pub fn get_from_seed(seed: &str) -> ::Public { 20 | TPublic::Pair::from_string(&format!("//{}", seed), None) 21 | .expect("static values are valid; qed") 22 | .public() 23 | } 24 | 25 | type AccountPublic = ::Signer; 26 | 27 | /// Generate an account ID from seed. 28 | pub fn get_account_id_from_seed(seed: &str) -> AccountId where 29 | AccountPublic: From<::Public> 30 | { 31 | AccountPublic::from(get_from_seed::(seed)).into_account() 32 | } 33 | 34 | /// Generate an Aura authority key. 35 | pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { 36 | ( 37 | get_from_seed::(s), 38 | get_from_seed::(s), 39 | ) 40 | } 41 | 42 | pub fn development_config() -> Result { 43 | let mut properties = Map::new(); 44 | properties.insert("tokenSymbol".into(), "TEST".into()); 45 | properties.insert("tokenDecimals".into(), 15.into()); 46 | 47 | let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; 48 | 49 | Ok(ChainSpec::from_genesis( 50 | // Name 51 | "Development", 52 | // ID 53 | "dev", 54 | ChainType::Development, 55 | move || { 56 | testnet_genesis( 57 | wasm_binary, 58 | // Initial PoA authorities 59 | vec![ 60 | authority_keys_from_seed("Alice"), 61 | ], 62 | // Sudo account 63 | get_account_id_from_seed::("Alice"), 64 | // Pre-funded accounts 65 | vec![ 66 | get_account_id_from_seed::("Alice"), 67 | get_account_id_from_seed::("Bob"), 68 | get_account_id_from_seed::("Alice//stash"), 69 | get_account_id_from_seed::("Bob//stash"), 70 | ], 71 | true, 72 | ) 73 | }, 74 | // Bootnodes 75 | vec![], 76 | // Telemetry 77 | None, 78 | // Protocol ID 79 | None, 80 | // Properties 81 | Some(properties), 82 | // Extensions 83 | None, 84 | )) 85 | } 86 | 87 | pub fn local_testnet_config() -> Result { 88 | let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; 89 | 90 | Ok(ChainSpec::from_genesis( 91 | // Name 92 | "Local Testnet", 93 | // ID 94 | "local_testnet", 95 | ChainType::Local, 96 | move || { 97 | testnet_genesis( 98 | wasm_binary, 99 | // Initial PoA authorities 100 | vec![ 101 | authority_keys_from_seed("Alice"), 102 | authority_keys_from_seed("Bob"), 103 | ], 104 | // Sudo account 105 | get_account_id_from_seed::("Alice"), 106 | // Pre-funded accounts 107 | vec![ 108 | get_account_id_from_seed::("Alice"), 109 | get_account_id_from_seed::("Bob"), 110 | get_account_id_from_seed::("Charlie"), 111 | get_account_id_from_seed::("Dave"), 112 | get_account_id_from_seed::("Eve"), 113 | get_account_id_from_seed::("Ferdie"), 114 | get_account_id_from_seed::("Alice//stash"), 115 | get_account_id_from_seed::("Bob//stash"), 116 | get_account_id_from_seed::("Charlie//stash"), 117 | get_account_id_from_seed::("Dave//stash"), 118 | get_account_id_from_seed::("Eve//stash"), 119 | get_account_id_from_seed::("Ferdie//stash"), 120 | ], 121 | true, 122 | ) 123 | }, 124 | // Bootnodes 125 | vec![], 126 | // Telemetry 127 | None, 128 | // Protocol ID 129 | None, 130 | // Properties 131 | None, 132 | // Extensions 133 | None, 134 | )) 135 | } 136 | 137 | /// Configure initial storage state for FRAME modules. 138 | fn testnet_genesis( 139 | wasm_binary: &[u8], 140 | initial_authorities: Vec<(AuraId, GrandpaId)>, 141 | root_key: AccountId, 142 | endowed_accounts: Vec, 143 | _enable_println: bool, 144 | ) -> GenesisConfig { 145 | GenesisConfig { 146 | frame_system: Some(SystemConfig { 147 | // Add Wasm runtime to storage. 148 | code: wasm_binary.to_vec(), 149 | changes_trie_config: Default::default(), 150 | }), 151 | pallet_balances: Some(BalancesConfig { 152 | // Configure endowed accounts with initial balance of 1 << 60. 153 | balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), 154 | }), 155 | pallet_aura: Some(AuraConfig { 156 | authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), 157 | }), 158 | pallet_grandpa: Some(GrandpaConfig { 159 | authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), 160 | }), 161 | pallet_sudo: Some(SudoConfig { 162 | // Assign network admin rights. 163 | key: root_key, 164 | }), 165 | orml_tokens: Some(TokensConfig { 166 | endowed_accounts: endowed_accounts 167 | .iter() 168 | .flat_map(|x| { 169 | vec![ 170 | (x.clone(), CurrencyId::DOT, 10u128.pow(16)), 171 | (x.clone(), CurrencyId::BTC, 10u128.pow(16)), 172 | ] 173 | }) 174 | .collect(), 175 | }), 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /node/src/cli.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | use sc_cli::RunCmd; 3 | 4 | #[derive(Debug, StructOpt)] 5 | pub struct Cli { 6 | #[structopt(subcommand)] 7 | pub subcommand: Option, 8 | 9 | #[structopt(flatten)] 10 | pub run: RunCmd, 11 | } 12 | 13 | #[derive(Debug, StructOpt)] 14 | pub enum Subcommand { 15 | /// Key management cli utilities 16 | Key(sc_cli::KeySubcommand), 17 | /// Build a chain specification. 18 | BuildSpec(sc_cli::BuildSpecCmd), 19 | 20 | /// Validate blocks. 21 | CheckBlock(sc_cli::CheckBlockCmd), 22 | 23 | /// Export blocks. 24 | ExportBlocks(sc_cli::ExportBlocksCmd), 25 | 26 | /// Export the state of a given block into a chain spec. 27 | ExportState(sc_cli::ExportStateCmd), 28 | 29 | /// Import blocks. 30 | ImportBlocks(sc_cli::ImportBlocksCmd), 31 | 32 | /// Remove the whole chain. 33 | PurgeChain(sc_cli::PurgeChainCmd), 34 | 35 | /// Revert the chain to a previous state. 36 | Revert(sc_cli::RevertCmd), 37 | 38 | /// The custom benchmark subcommmand benchmarking runtime pallets. 39 | #[structopt(name = "benchmark", about = "Benchmark runtime pallets.")] 40 | Benchmark(frame_benchmarking_cli::BenchmarkCmd), 41 | } 42 | -------------------------------------------------------------------------------- /node/src/command.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | use crate::{chain_spec, service}; 19 | use crate::cli::{Cli, Subcommand}; 20 | use sc_cli::{SubstrateCli, RuntimeVersion, Role, ChainSpec}; 21 | use sc_service::PartialComponents; 22 | use node_template_runtime::Block; 23 | 24 | impl SubstrateCli for Cli { 25 | fn impl_name() -> String { 26 | "Substrate Node".into() 27 | } 28 | 29 | fn impl_version() -> String { 30 | env!("SUBSTRATE_CLI_IMPL_VERSION").into() 31 | } 32 | 33 | fn description() -> String { 34 | env!("CARGO_PKG_DESCRIPTION").into() 35 | } 36 | 37 | fn author() -> String { 38 | env!("CARGO_PKG_AUTHORS").into() 39 | } 40 | 41 | fn support_url() -> String { 42 | "support.anonymous.an".into() 43 | } 44 | 45 | fn copyright_start_year() -> i32 { 46 | 2017 47 | } 48 | 49 | fn load_spec(&self, id: &str) -> Result, String> { 50 | Ok(match id { 51 | "dev" => Box::new(chain_spec::development_config()?), 52 | "" | "local" => Box::new(chain_spec::local_testnet_config()?), 53 | path => Box::new(chain_spec::ChainSpec::from_json_file( 54 | std::path::PathBuf::from(path), 55 | )?), 56 | }) 57 | } 58 | 59 | fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { 60 | &node_template_runtime::VERSION 61 | } 62 | } 63 | 64 | /// Parse and run command line arguments 65 | pub fn run() -> sc_cli::Result<()> { 66 | let cli = Cli::from_args(); 67 | 68 | match &cli.subcommand { 69 | Some(Subcommand::Key(cmd)) => cmd.run(&cli), 70 | Some(Subcommand::BuildSpec(cmd)) => { 71 | let runner = cli.create_runner(cmd)?; 72 | runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) 73 | }, 74 | Some(Subcommand::CheckBlock(cmd)) => { 75 | let runner = cli.create_runner(cmd)?; 76 | runner.async_run(|config| { 77 | let PartialComponents { client, task_manager, import_queue, ..} 78 | = service::new_partial(&config)?; 79 | Ok((cmd.run(client, import_queue), task_manager)) 80 | }) 81 | }, 82 | Some(Subcommand::ExportBlocks(cmd)) => { 83 | let runner = cli.create_runner(cmd)?; 84 | runner.async_run(|config| { 85 | let PartialComponents { client, task_manager, ..} 86 | = service::new_partial(&config)?; 87 | Ok((cmd.run(client, config.database), task_manager)) 88 | }) 89 | }, 90 | Some(Subcommand::ExportState(cmd)) => { 91 | let runner = cli.create_runner(cmd)?; 92 | runner.async_run(|config| { 93 | let PartialComponents { client, task_manager, ..} 94 | = service::new_partial(&config)?; 95 | Ok((cmd.run(client, config.chain_spec), task_manager)) 96 | }) 97 | }, 98 | Some(Subcommand::ImportBlocks(cmd)) => { 99 | let runner = cli.create_runner(cmd)?; 100 | runner.async_run(|config| { 101 | let PartialComponents { client, task_manager, import_queue, ..} 102 | = service::new_partial(&config)?; 103 | Ok((cmd.run(client, import_queue), task_manager)) 104 | }) 105 | }, 106 | Some(Subcommand::PurgeChain(cmd)) => { 107 | let runner = cli.create_runner(cmd)?; 108 | runner.sync_run(|config| cmd.run(config.database)) 109 | }, 110 | Some(Subcommand::Revert(cmd)) => { 111 | let runner = cli.create_runner(cmd)?; 112 | runner.async_run(|config| { 113 | let PartialComponents { client, task_manager, backend, ..} 114 | = service::new_partial(&config)?; 115 | Ok((cmd.run(client, backend), task_manager)) 116 | }) 117 | }, 118 | Some(Subcommand::Benchmark(cmd)) => { 119 | if cfg!(feature = "runtime-benchmarks") { 120 | let runner = cli.create_runner(cmd)?; 121 | 122 | runner.sync_run(|config| cmd.run::(config)) 123 | } else { 124 | Err("Benchmarking wasn't enabled when building the node. \ 125 | You can enable it with `--features runtime-benchmarks`.".into()) 126 | } 127 | }, 128 | None => { 129 | let runner = cli.create_runner(&cli.run)?; 130 | runner.run_node_until_exit(|config| async move { 131 | match config.role { 132 | Role::Light => service::new_light(config), 133 | _ => service::new_full(config), 134 | } 135 | .map_err(sc_cli::Error::Service) 136 | }) 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /node/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod chain_spec; 2 | pub mod service; 3 | pub mod rpc; 4 | -------------------------------------------------------------------------------- /node/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Substrate Node Template CLI library. 2 | #![warn(missing_docs)] 3 | 4 | mod chain_spec; 5 | #[macro_use] 6 | mod service; 7 | mod cli; 8 | mod command; 9 | mod rpc; 10 | 11 | fn main() -> sc_cli::Result<()> { 12 | command::run() 13 | } 14 | -------------------------------------------------------------------------------- /node/src/rpc.rs: -------------------------------------------------------------------------------- 1 | //! A collection of node-specific RPC methods. 2 | //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer 3 | //! used by Substrate nodes. This file extends those RPC definitions with 4 | //! capabilities that are specific to this project's runtime configuration. 5 | 6 | #![warn(missing_docs)] 7 | 8 | use std::sync::Arc; 9 | 10 | use node_template_runtime::{opaque::Block, AccountId, Balance, Index}; 11 | use sp_api::ProvideRuntimeApi; 12 | use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; 13 | use sp_block_builder::BlockBuilder; 14 | pub use sc_rpc_api::DenyUnsafe; 15 | use sp_transaction_pool::TransactionPool; 16 | 17 | 18 | /// Full client dependencies. 19 | pub struct FullDeps { 20 | /// The client instance to use. 21 | pub client: Arc, 22 | /// Transaction pool instance. 23 | pub pool: Arc

, 24 | /// Whether to deny unsafe calls 25 | pub deny_unsafe: DenyUnsafe, 26 | } 27 | 28 | /// Instantiate all full RPC extensions. 29 | pub fn create_full(deps: FullDeps) -> jsonrpc_core::IoHandler 30 | where 31 | C: ProvideRuntimeApi, 32 | C: HeaderBackend + HeaderMetadata + 'static, 33 | C: Send + Sync + 'static, 34 | C::Api: substrate_frame_rpc_system::AccountNonceApi, 35 | C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, 36 | C::Api: BlockBuilder, 37 | P: TransactionPool + 'static, 38 | { 39 | use substrate_frame_rpc_system::{FullSystem, SystemApi}; 40 | use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; 41 | 42 | let mut io = jsonrpc_core::IoHandler::default(); 43 | let FullDeps { 44 | client, 45 | pool, 46 | deny_unsafe, 47 | } = deps; 48 | 49 | io.extend_with(SystemApi::to_delegate(FullSystem::new( 50 | client.clone(), 51 | pool, 52 | deny_unsafe, 53 | ))); 54 | 55 | io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new( 56 | client.clone(), 57 | ))); 58 | 59 | // Extend this RPC with a custom API by using the following syntax. 60 | // `YourRpcStruct` should have a reference to a client, which is needed 61 | // to call into the runtime. 62 | // `io.extend_with(YourRpcTrait::to_delegate(YourRpcStruct::new(ReferenceToClient, ...)));` 63 | 64 | io 65 | } 66 | -------------------------------------------------------------------------------- /node/src/service.rs: -------------------------------------------------------------------------------- 1 | //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. 2 | 3 | use std::sync::Arc; 4 | use std::time::Duration; 5 | use sc_client_api::{ExecutorProvider, RemoteBackend}; 6 | use node_template_runtime::{self, opaque::Block, RuntimeApi}; 7 | use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; 8 | use sp_inherents::InherentDataProviders; 9 | use sc_executor::native_executor_instance; 10 | pub use sc_executor::NativeExecutor; 11 | use sc_finality_grandpa::SharedVoterState; 12 | use sc_keystore::LocalKeystore; 13 | use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; 14 | 15 | 16 | 17 | // Our native executor instance. 18 | native_executor_instance!( 19 | pub Executor, 20 | node_template_runtime::api::dispatch, 21 | node_template_runtime::native_version, 22 | frame_benchmarking::benchmarking::HostFunctions, 23 | ); 24 | 25 | type FullClient = sc_service::TFullClient; 26 | type FullBackend = sc_service::TFullBackend; 27 | type FullSelectChain = sc_consensus::LongestChain; 28 | 29 | pub fn new_partial( 30 | config: &Configuration, 31 | ) -> Result< 32 | sc_service::PartialComponents< 33 | FullClient, 34 | FullBackend, 35 | FullSelectChain, 36 | sp_consensus::DefaultImportQueue, 37 | sc_transaction_pool::FullPool, 38 | ( 39 | sc_consensus_aura::AuraBlockImport< 40 | Block, 41 | FullClient, 42 | sc_finality_grandpa::GrandpaBlockImport< 43 | FullBackend, 44 | Block, 45 | FullClient, 46 | FullSelectChain, 47 | >, 48 | AuraPair, 49 | >, 50 | sc_finality_grandpa::LinkHalf, 51 | ), 52 | >, 53 | ServiceError, 54 | > { 55 | if config.keystore_remote.is_some() { 56 | return Err(ServiceError::Other(format!( 57 | "Remote Keystores are not supported." 58 | ))); 59 | } 60 | let inherent_data_providers = InherentDataProviders::new(); 61 | 62 | let (client, backend, keystore_container, task_manager) = 63 | sc_service::new_full_parts::(&config)?; 64 | let client = Arc::new(client); 65 | 66 | let select_chain = sc_consensus::LongestChain::new(backend.clone()); 67 | 68 | let transaction_pool = sc_transaction_pool::BasicPool::new_full( 69 | config.transaction_pool.clone(), 70 | config.role.is_authority().into(), 71 | config.prometheus_registry(), 72 | task_manager.spawn_handle(), 73 | client.clone(), 74 | ); 75 | 76 | let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( 77 | client.clone(), 78 | &(client.clone() as Arc<_>), 79 | select_chain.clone(), 80 | )?; 81 | 82 | let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new( 83 | grandpa_block_import.clone(), 84 | client.clone(), 85 | ); 86 | 87 | let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _, _>( 88 | sc_consensus_aura::slot_duration(&*client)?, 89 | aura_block_import.clone(), 90 | Some(Box::new(grandpa_block_import.clone())), 91 | client.clone(), 92 | inherent_data_providers.clone(), 93 | &task_manager.spawn_handle(), 94 | config.prometheus_registry(), 95 | sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()), 96 | )?; 97 | 98 | Ok(sc_service::PartialComponents { 99 | client, 100 | backend, 101 | task_manager, 102 | import_queue, 103 | keystore_container, 104 | select_chain, 105 | transaction_pool, 106 | inherent_data_providers, 107 | other: (aura_block_import, grandpa_link), 108 | }) 109 | } 110 | 111 | fn remote_keystore(_url: &String) -> Result, &'static str> { 112 | // FIXME: here would the concrete keystore be built, 113 | // must return a concrete type (NOT `LocalKeystore`) that 114 | // implements `CryptoStore` and `SyncCryptoStore` 115 | Err("Remote Keystore not supported.") 116 | } 117 | 118 | /// Builds a new service for a full client. 119 | pub fn new_full(mut config: Configuration) -> Result { 120 | let sc_service::PartialComponents { 121 | client, 122 | backend, 123 | mut task_manager, 124 | import_queue, 125 | mut keystore_container, 126 | select_chain, 127 | transaction_pool, 128 | inherent_data_providers, 129 | other: (block_import, grandpa_link), 130 | } = new_partial(&config)?; 131 | 132 | if let Some(url) = &config.keystore_remote { 133 | match remote_keystore(url) { 134 | Ok(k) => keystore_container.set_remote_keystore(k), 135 | Err(e) => { 136 | return Err(ServiceError::Other(format!( 137 | "Error hooking up remote keystore for {}: {}", 138 | url, e 139 | ))) 140 | } 141 | }; 142 | } 143 | 144 | config 145 | .network 146 | .extra_sets 147 | .push(sc_finality_grandpa::grandpa_peers_set_config()); 148 | 149 | let (network, network_status_sinks, system_rpc_tx, network_starter) = 150 | sc_service::build_network(sc_service::BuildNetworkParams { 151 | config: &config, 152 | client: client.clone(), 153 | transaction_pool: transaction_pool.clone(), 154 | spawn_handle: task_manager.spawn_handle(), 155 | import_queue, 156 | on_demand: None, 157 | block_announce_validator_builder: None, 158 | })?; 159 | 160 | if config.offchain_worker.enabled { 161 | sc_service::build_offchain_workers( 162 | &config, 163 | backend.clone(), 164 | task_manager.spawn_handle(), 165 | client.clone(), 166 | network.clone(), 167 | ); 168 | } 169 | 170 | let role = config.role.clone(); 171 | let force_authoring = config.force_authoring; 172 | let backoff_authoring_blocks: Option<()> = None; 173 | let name = config.network.node_name.clone(); 174 | let enable_grandpa = !config.disable_grandpa; 175 | let prometheus_registry = config.prometheus_registry().cloned(); 176 | 177 | let rpc_extensions_builder = { 178 | let client = client.clone(); 179 | let pool = transaction_pool.clone(); 180 | 181 | Box::new(move |deny_unsafe, _| { 182 | let deps = crate::rpc::FullDeps { 183 | client: client.clone(), 184 | pool: pool.clone(), 185 | deny_unsafe, 186 | }; 187 | 188 | crate::rpc::create_full(deps) 189 | }) 190 | }; 191 | 192 | let (_rpc_handlers, telemetry_connection_notifier) = 193 | sc_service::spawn_tasks(sc_service::SpawnTasksParams { 194 | network: network.clone(), 195 | client: client.clone(), 196 | keystore: keystore_container.sync_keystore(), 197 | task_manager: &mut task_manager, 198 | transaction_pool: transaction_pool.clone(), 199 | rpc_extensions_builder, 200 | on_demand: None, 201 | remote_blockchain: None, 202 | backend, 203 | network_status_sinks, 204 | system_rpc_tx, 205 | config, 206 | })?; 207 | 208 | if role.is_authority() { 209 | let proposer_factory = sc_basic_authorship::ProposerFactory::new( 210 | task_manager.spawn_handle(), 211 | client.clone(), 212 | transaction_pool, 213 | prometheus_registry.as_ref(), 214 | ); 215 | 216 | let can_author_with = 217 | sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()); 218 | 219 | let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _, _>( 220 | sc_consensus_aura::slot_duration(&*client)?, 221 | client.clone(), 222 | select_chain, 223 | block_import, 224 | proposer_factory, 225 | network.clone(), 226 | inherent_data_providers.clone(), 227 | force_authoring, 228 | backoff_authoring_blocks, 229 | keystore_container.sync_keystore(), 230 | can_author_with, 231 | )?; 232 | 233 | // the AURA authoring task is considered essential, i.e. if it 234 | // fails we take down the service with it. 235 | task_manager 236 | .spawn_essential_handle() 237 | .spawn_blocking("aura", aura); 238 | } 239 | 240 | // if the node isn't actively participating in consensus then it doesn't 241 | // need a keystore, regardless of which protocol we use below. 242 | let keystore = if role.is_authority() { 243 | Some(keystore_container.sync_keystore()) 244 | } else { 245 | None 246 | }; 247 | 248 | let grandpa_config = sc_finality_grandpa::Config { 249 | // FIXME #1578 make this available through chainspec 250 | gossip_duration: Duration::from_millis(333), 251 | justification_period: 512, 252 | name: Some(name), 253 | observer_enabled: false, 254 | keystore, 255 | is_authority: role.is_network_authority(), 256 | }; 257 | 258 | if enable_grandpa { 259 | // start the full GRANDPA voter 260 | // NOTE: non-authorities could run the GRANDPA observer protocol, but at 261 | // this point the full voter should provide better guarantees of block 262 | // and vote data availability than the observer. The observer has not 263 | // been tested extensively yet and having most nodes in a network run it 264 | // could lead to finality stalls. 265 | let grandpa_config = sc_finality_grandpa::GrandpaParams { 266 | config: grandpa_config, 267 | link: grandpa_link, 268 | network, 269 | voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(), 270 | prometheus_registry, 271 | shared_voter_state: SharedVoterState::empty(), 272 | telemetry_on_connect: telemetry_connection_notifier.map(|x| x.on_connect_stream()), 273 | }; 274 | 275 | // the GRANDPA voter task is considered infallible, i.e. 276 | // if it fails we take down the service with it. 277 | task_manager.spawn_essential_handle().spawn_blocking( 278 | "grandpa-voter", 279 | sc_finality_grandpa::run_grandpa_voter(grandpa_config)?, 280 | ); 281 | } 282 | 283 | network_starter.start_network(); 284 | Ok(task_manager) 285 | } 286 | 287 | /// Builds a new service for a light client. 288 | pub fn new_light(mut config: Configuration) -> Result { 289 | let (client, backend, keystore_container, mut task_manager, on_demand) = 290 | sc_service::new_light_parts::(&config)?; 291 | 292 | config 293 | .network 294 | .extra_sets 295 | .push(sc_finality_grandpa::grandpa_peers_set_config()); 296 | 297 | let select_chain = sc_consensus::LongestChain::new(backend.clone()); 298 | 299 | let transaction_pool = Arc::new(sc_transaction_pool::BasicPool::new_light( 300 | config.transaction_pool.clone(), 301 | config.prometheus_registry(), 302 | task_manager.spawn_handle(), 303 | client.clone(), 304 | on_demand.clone(), 305 | )); 306 | 307 | let (grandpa_block_import, _) = sc_finality_grandpa::block_import( 308 | client.clone(), 309 | &(client.clone() as Arc<_>), 310 | select_chain.clone(), 311 | )?; 312 | 313 | let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new( 314 | grandpa_block_import.clone(), 315 | client.clone(), 316 | ); 317 | 318 | let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _, _>( 319 | sc_consensus_aura::slot_duration(&*client)?, 320 | aura_block_import, 321 | Some(Box::new(grandpa_block_import)), 322 | client.clone(), 323 | InherentDataProviders::new(), 324 | &task_manager.spawn_handle(), 325 | config.prometheus_registry(), 326 | sp_consensus::NeverCanAuthor, 327 | )?; 328 | 329 | let (network, network_status_sinks, system_rpc_tx, network_starter) = 330 | sc_service::build_network(sc_service::BuildNetworkParams { 331 | config: &config, 332 | client: client.clone(), 333 | transaction_pool: transaction_pool.clone(), 334 | spawn_handle: task_manager.spawn_handle(), 335 | import_queue, 336 | on_demand: Some(on_demand.clone()), 337 | block_announce_validator_builder: None, 338 | })?; 339 | 340 | if config.offchain_worker.enabled { 341 | sc_service::build_offchain_workers( 342 | &config, 343 | backend.clone(), 344 | task_manager.spawn_handle(), 345 | client.clone(), 346 | network.clone(), 347 | ); 348 | } 349 | 350 | sc_service::spawn_tasks(sc_service::SpawnTasksParams { 351 | remote_blockchain: Some(backend.remote_blockchain()), 352 | transaction_pool, 353 | task_manager: &mut task_manager, 354 | on_demand: Some(on_demand), 355 | rpc_extensions_builder: Box::new(|_, _| ()), 356 | config, 357 | client, 358 | keystore: keystore_container.sync_keystore(), 359 | backend, 360 | network, 361 | network_status_sinks, 362 | system_rpc_tx, 363 | })?; 364 | 365 | network_starter.start_network(); 366 | 367 | Ok(task_manager) 368 | } 369 | -------------------------------------------------------------------------------- /pallets/exchange/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Substrate DevHub '] 3 | description = 'FRAME pallet exchange for defining custom runtime logic.' 4 | edition = '2018' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-exchange' 8 | readme = 'README.md' 9 | repository = 'https://github.com/substrate-developer-hub/substrate-node-template/' 10 | version = '3.0.0' 11 | 12 | [package.metadata.docs.rs] 13 | targets = ['x86_64-unknown-linux-gnu'] 14 | 15 | [dependencies] 16 | codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0' } 17 | frame-system = { default-features = false, version = '3.0.0' } 18 | frame-support = { default-features = false, version = '3.0.0' } 19 | frame-benchmarking = { default-features = false, optional = true, version = '3.1.0' } 20 | sp-std = { default-features = false, version = '3.0.0' } 21 | pallet-balances = { default-features = false, version = '3.0.0' } 22 | 23 | orml-currencies = {default-features = false, version = '0.4.0'} 24 | orml-tokens = {default-features = false, version = "0.4.0"} 25 | orml-traits = { default-features = false, version = '0.4.0' } 26 | orml-utilities = { default-features = false, version = '0.4.0' } 27 | 28 | [dev-dependencies] 29 | serde = { version = "1.0.119" } 30 | sp-core = { default-features = false, version = '3.0.0' } 31 | sp-io = { default-features = false, version = '3.0.0' } 32 | sp-runtime = { default-features = false, version = '3.0.0' } 33 | 34 | [features] 35 | default = ['std'] 36 | std = [ 37 | 'codec/std', 38 | 'frame-support/std', 39 | 'frame-system/std', 40 | 'frame-benchmarking/std', 41 | 'sp-std/std', 42 | 'sp-runtime/std', 43 | 'orml-currencies/std', 44 | 'orml-tokens/std', 45 | 'orml-traits/std', 46 | 'orml-utilities/std', 47 | 'pallet-balances/std', 48 | ] 49 | runtime-benchmarks = [ 50 | 'frame-benchmarking', 51 | 'frame-support/runtime-benchmarks', 52 | 'frame-system/runtime-benchmarks', 53 | ] 54 | # Note: frame-support `try-runtime` feature is released after v3. 55 | # Uncomment the following line when `frame-support` version > `3.0.0`. 56 | # try-runtime = ['frame-support/try-runtime'] 57 | -------------------------------------------------------------------------------- /pallets/exchange/src/benchmarking.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarking setup for pallet-template 2 | 3 | use super::*; 4 | 5 | use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; 6 | use frame_system::RawOrigin; 7 | use sp_std::{boxed::Box, vec, vec::Vec}; 8 | 9 | #[allow(unused)] 10 | use crate::Module as Template; 11 | 12 | benchmarks! { 13 | do_something { 14 | let s in 0 .. 100; 15 | let caller: T::AccountId = whitelisted_caller(); 16 | }: _(RawOrigin::Signed(caller), s) 17 | verify { 18 | assert_eq!(Something::::get(), Some(s)); 19 | } 20 | } 21 | 22 | impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test,); 23 | -------------------------------------------------------------------------------- /pallets/exchange/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | use codec::{Decode, Encode}; 4 | use frame_support::sp_runtime::{ 5 | traits::{AtLeast32BitUnsigned, Bounded, CheckedAdd, MaybeSerializeDeserialize, One, Zero}, 6 | RuntimeDebug, 7 | }; 8 | use frame_support::{traits::BalanceStatus, Parameter, transactional}; 9 | use frame_system::ensure_signed; 10 | use orml_traits::{MultiCurrency, MultiReservableCurrency}; 11 | /// Edit this file to define custom logic or remove it if it is not needed. 12 | /// Learn more about FRAME and the core library of Substrate FRAME pallets: 13 | /// 14 | pub use pallet::*; 15 | 16 | #[cfg(test)] 17 | mod mock; 18 | 19 | #[cfg(test)] 20 | mod tests; 21 | 22 | #[cfg(feature = "runtime-benchmarks")] 23 | mod benchmarking; 24 | 25 | #[derive(Encode, Decode, Clone, RuntimeDebug, Eq, PartialEq)] 26 | pub struct Order { 27 | pub base_currency_id: CurrencyId, 28 | #[codec(compact)] 29 | pub base_amount: Balance, 30 | pub target_currency_id: CurrencyId, 31 | #[codec(compact)] 32 | pub target_amount: Balance, 33 | pub owner: AccountId, 34 | } 35 | 36 | type BalanceOf = 37 | <::Currency as MultiCurrency<::AccountId>>::Balance; 38 | type CurrencyIdOf = 39 | <::Currency as MultiCurrency<::AccountId>>::CurrencyId; 40 | type OrderOf = Order, BalanceOf, ::AccountId>; 41 | 42 | #[frame_support::pallet] 43 | pub mod pallet { 44 | use super::*; 45 | use frame_support::{dispatch::DispatchResultWithPostInfo, dispatch::DispatchResult, pallet_prelude::*}; 46 | use frame_system::pallet_prelude::*; 47 | 48 | /// Configure the pallet by specifying the parameters and types on which it depends. 49 | #[pallet::config] 50 | pub trait Config: frame_system::Config { 51 | /// Because this pallet emits events, it depends on the runtime's definition of an event. 52 | type Event: From> + IsType<::Event>; 53 | /// Currency 54 | type Currency: MultiReservableCurrency; 55 | /// OrderId 56 | type OrderId: Parameter 57 | + AtLeast32BitUnsigned 58 | + Default 59 | + Copy 60 | + MaybeSerializeDeserialize 61 | + Bounded; 62 | } 63 | 64 | #[pallet::pallet] 65 | #[pallet::generate_store(pub(super) trait Store)] 66 | pub struct Pallet(_); 67 | 68 | #[pallet::storage] 69 | #[pallet::getter(fn orders)] 70 | pub type Orders = StorageMap<_, Twox64Concat, T::OrderId, OrderOf>; 71 | 72 | #[pallet::storage] 73 | #[pallet::getter(fn next_orderid)] 74 | pub type NextOrderId = StorageValue<_, T::OrderId>; 75 | 76 | // Pallets use events to inform users when important changes are made. 77 | // https://substrate.dev/docs/en/knowledgebase/runtime/events 78 | #[pallet::event] 79 | #[pallet::metadata(T::AccountId = "AccountId", T::OrderId = "OrderId", OrderOf = "Order", BalanceOf = "Balance")] 80 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 81 | pub enum Event { 82 | OrderCreated(T::OrderId, OrderOf), 83 | OrderTaken(T::AccountId, T::OrderId, OrderOf), 84 | OrderCancelled(T::OrderId), 85 | } 86 | 87 | // Errors inform users that something went wrong. 88 | #[pallet::error] 89 | pub enum Error { 90 | OrderIdOverflow, 91 | InvalidOrderId, 92 | InsufficientBalance, 93 | NotOwner, 94 | } 95 | 96 | #[pallet::hooks] 97 | impl Hooks> for Pallet {} 98 | 99 | // Dispatchable functions allows users to interact with the pallet and invoke state changes. 100 | // These functions materialize as "extrinsics", which are often compared to transactions. 101 | // Dispatchable functions must be annotated with a weight and must return a DispatchResult. 102 | #[pallet::call] 103 | impl Pallet { 104 | #[pallet::weight(10_000)] 105 | pub fn submit_order( 106 | origin: OriginFor, 107 | base_currency_id: CurrencyIdOf, 108 | base_amount: BalanceOf, 109 | target_currency_id: CurrencyIdOf, 110 | target_amount: BalanceOf, 111 | ) -> DispatchResultWithPostInfo { 112 | let who = ensure_signed(origin)?; 113 | 114 | NextOrderId::::try_mutate(|id| -> DispatchResultWithPostInfo { 115 | let order_id = id.unwrap_or_default(); 116 | 117 | let order = Order { 118 | base_currency_id, 119 | base_amount, 120 | target_currency_id, 121 | target_amount, 122 | owner: who.clone(), 123 | }; 124 | 125 | *id = Some( 126 | order_id 127 | .checked_add(&One::one()) 128 | .ok_or(Error::::OrderIdOverflow)?, 129 | ); 130 | 131 | T::Currency::reserve(base_currency_id, &who, base_amount)?; 132 | 133 | Orders::::insert(order_id, &order); 134 | 135 | Self::deposit_event(Event::OrderCreated(order_id, order)); 136 | Ok(().into()) 137 | })?; 138 | Ok(().into()) 139 | } 140 | 141 | #[pallet::weight(10_000)] 142 | #[transactional] 143 | pub fn take_order( 144 | origin: OriginFor, 145 | order_id: T::OrderId, 146 | ) -> DispatchResultWithPostInfo { 147 | let who = ensure_signed(origin)?; 148 | 149 | Orders::::try_mutate_exists(order_id, |order| -> DispatchResultWithPostInfo { 150 | let order = order.take().ok_or(Error::::InvalidOrderId)?; 151 | 152 | T::Currency::transfer( 153 | order.target_currency_id, 154 | &who, 155 | &order.owner, 156 | order.target_amount, 157 | )?; 158 | let val = T::Currency::repatriate_reserved( 159 | order.base_currency_id, 160 | &order.owner, 161 | &who, 162 | order.base_amount, 163 | BalanceStatus::Free, 164 | )?; 165 | ensure!(val.is_zero(), Error::::InsufficientBalance); 166 | 167 | Self::deposit_event(Event::OrderTaken(who, order_id, order)); 168 | Ok(().into()) 169 | })?; 170 | Ok(().into()) 171 | } 172 | 173 | #[pallet::weight(10_000)] 174 | pub fn cancel_order( 175 | origin: OriginFor, 176 | order_id: T::OrderId, 177 | ) -> DispatchResultWithPostInfo { 178 | let who = ensure_signed(origin)?; 179 | 180 | Orders::::try_mutate_exists(order_id, |order| -> DispatchResult { 181 | let order = order.take().ok_or(Error::::InvalidOrderId)?; 182 | 183 | ensure!(order.owner == who, Error::::NotOwner); 184 | 185 | Self::deposit_event(Event::OrderCancelled(order_id)); 186 | Ok(()) 187 | })?; 188 | Ok(().into()) 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /pallets/exchange/src/mock.rs: -------------------------------------------------------------------------------- 1 | use crate as pallet_exchange; 2 | use frame_support::parameter_types; 3 | use frame_support::sp_runtime::{ 4 | testing::Header, 5 | traits::{BlakeTwo256, IdentityLookup, Zero}, 6 | }; 7 | use frame_system as system; 8 | use sp_core::H256; 9 | 10 | use orml_traits::parameter_type_with_key; 11 | 12 | use frame_support::traits::GenesisBuild; 13 | 14 | pub type Amount = i128; 15 | pub type AccountId = u64; 16 | pub type Balance = u128; 17 | pub type CurrencyId = u128; 18 | pub type BlockNumber = u64; 19 | 20 | pub const ALICE: AccountId = 1; 21 | pub const BOB: AccountId = 2; 22 | 23 | pub const DOT: CurrencyId = 1; 24 | //pub const KSM: CurrencyId = 2; 25 | pub const BTC: CurrencyId = 3; 26 | 27 | type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; 28 | type Block = frame_system::mocking::MockBlock; 29 | 30 | // Configure a mock runtime to test the pallet. 31 | frame_support::construct_runtime!( 32 | pub enum Test where 33 | Block = Block, 34 | NodeBlock = Block, 35 | UncheckedExtrinsic = UncheckedExtrinsic, 36 | { 37 | System: frame_system::{Module, Call, Config, Storage, Event}, 38 | Balances: pallet_balances::{Module, Call, Storage, Config, Event}, 39 | ExchangeModule: pallet_exchange::{Module, Call, Storage, Event}, 40 | Currencies: orml_currencies::{Module, Call, Event}, 41 | Tokens: orml_tokens::{Module, Storage, Event, Config}, 42 | } 43 | ); 44 | 45 | parameter_types! { 46 | pub const BlockHashCount: u64 = 250; 47 | pub const SS58Prefix: u8 = 42; 48 | } 49 | 50 | impl system::Config for Test { 51 | type BaseCallFilter = (); 52 | type BlockWeights = (); 53 | type BlockLength = (); 54 | type DbWeight = (); 55 | type Origin = Origin; 56 | type Call = Call; 57 | type Index = u64; 58 | type BlockNumber = u64; 59 | type Hash = H256; 60 | type Hashing = BlakeTwo256; 61 | type AccountId = u64; 62 | type Lookup = IdentityLookup; 63 | type Header = Header; 64 | type Event = Event; 65 | type BlockHashCount = BlockHashCount; 66 | type Version = (); 67 | type PalletInfo = PalletInfo; 68 | type AccountData = pallet_balances::AccountData; 69 | type OnNewAccount = (); 70 | type OnKilledAccount = (); 71 | type SystemWeightInfo = (); 72 | type SS58Prefix = SS58Prefix; 73 | } 74 | 75 | parameter_type_with_key! { 76 | pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { 77 | Zero::zero() 78 | }; 79 | } 80 | 81 | impl orml_tokens::Config for Test { 82 | type Event = Event; 83 | type Balance = Balance; 84 | type Amount = Amount; 85 | type CurrencyId = CurrencyId; 86 | type WeightInfo = (); 87 | type ExistentialDeposits = ExistentialDeposits; 88 | type OnDust = (); 89 | } 90 | 91 | parameter_types! { 92 | pub const GetNativeCurrencyId: CurrencyId = 0; 93 | } 94 | 95 | impl orml_currencies::Config for Test { 96 | type Event = Event; 97 | type MultiCurrency = Tokens; 98 | type NativeCurrency = 99 | orml_currencies::BasicCurrencyAdapter; 100 | type GetNativeCurrencyId = GetNativeCurrencyId; 101 | type WeightInfo = (); 102 | } 103 | 104 | parameter_types! { 105 | pub const ExistentialDeposit: u128 = 500; 106 | pub const MaxLocks: u32 = 50; 107 | } 108 | 109 | impl pallet_balances::Config for Test { 110 | type MaxLocks = MaxLocks; 111 | type Balance = Balance; 112 | type Event = Event; 113 | type DustRemoval = (); 114 | type ExistentialDeposit = ExistentialDeposit; 115 | type AccountStore = System; 116 | type WeightInfo = (); 117 | } 118 | 119 | impl pallet_exchange::Config for Test { 120 | type Event = Event; 121 | type Currency = Currencies; 122 | type OrderId = u32; 123 | } 124 | 125 | // Build genesis storage according to the mock runtime. 126 | // pub fn new_test_ext() -> sp_io::TestExternalities { 127 | // system::GenesisConfig::default().build_storage::().unwrap().into() 128 | // } 129 | 130 | pub struct ExtBuilder { 131 | endowed_accounts: Vec<(AccountId, CurrencyId, Balance)>, 132 | } 133 | 134 | impl Default for ExtBuilder { 135 | fn default() -> Self { 136 | Self { 137 | endowed_accounts: vec![ 138 | (ALICE, DOT, 1000_000_000_000_000u128), 139 | (BOB, DOT, 1000_000_000_000_000u128), 140 | (ALICE, BTC, 1000_000_000_000_000u128), 141 | (BOB, BTC, 1000_000_000_000_000u128), 142 | ], 143 | } 144 | } 145 | } 146 | 147 | impl ExtBuilder { 148 | pub fn build(self) -> sp_io::TestExternalities { 149 | let mut t = frame_system::GenesisConfig::default() 150 | .build_storage::() 151 | .unwrap(); 152 | 153 | orml_tokens::GenesisConfig:: { 154 | endowed_accounts: self.endowed_accounts, 155 | } 156 | .assimilate_storage(&mut t) 157 | .unwrap(); 158 | 159 | t.into() 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /pallets/exchange/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | pub use crate::mock::{ 3 | Currencies, Event, ExchangeModule, ExtBuilder, Origin, System, Tokens, ALICE, BOB, 4 | }; 5 | use crate::{mock::*, Error}; 6 | use frame_support::{assert_noop, assert_ok}; 7 | 8 | const ENDOWED_AMOUNT: u128 = 1_000_000_000_000_000; 9 | 10 | fn new_test_ext() -> sp_io::TestExternalities { 11 | let mut ext = ExtBuilder::default().build(); 12 | ext.execute_with(|| System::set_block_number(1)); 13 | ext 14 | } 15 | 16 | fn events() -> Vec { 17 | let evt = System::events() 18 | .into_iter() 19 | .map(|evt| evt.event) 20 | .collect::>(); 21 | System::reset_events(); 22 | evt 23 | } 24 | 25 | #[test] 26 | fn test_submit_order() { 27 | new_test_ext().execute_with(|| { 28 | //sell amount <= balance 29 | assert_ok!(ExchangeModule::submit_order( 30 | Origin::signed(ALICE), 31 | DOT, 32 | 10, 33 | BTC, 34 | 1 35 | )); 36 | 37 | assert_eq!(Tokens::free_balance(DOT, &ALICE), ENDOWED_AMOUNT - 10); 38 | assert_eq!(Tokens::free_balance(BTC, &ALICE), ENDOWED_AMOUNT); 39 | 40 | assert_eq!( 41 | events().as_slice(), 42 | [Event::pallet_exchange(crate::Event::OrderCreated( 43 | 0, 44 | Order { 45 | base_currency_id: DOT, 46 | base_amount: 10, 47 | target_currency_id: BTC, 48 | target_amount: 1, 49 | owner: ALICE 50 | } 51 | )),] 52 | ); 53 | }); 54 | } 55 | 56 | #[test] 57 | fn test_take_order() { 58 | new_test_ext().execute_with(|| { 59 | //id not exist 60 | assert_noop!( 61 | ExchangeModule::take_order(Origin::signed(BOB), 0), 62 | Error::::InvalidOrderId 63 | ); 64 | 65 | //id exist 66 | assert_ok!(ExchangeModule::submit_order( 67 | Origin::signed(ALICE), 68 | DOT, 69 | 10, 70 | BTC, 71 | 1 72 | )); 73 | assert_eq!(Tokens::free_balance(DOT, &ALICE), ENDOWED_AMOUNT - 10); 74 | assert_eq!(Tokens::free_balance(BTC, &ALICE), ENDOWED_AMOUNT); 75 | 76 | assert_ok!(ExchangeModule::take_order(Origin::signed(BOB), 0)); 77 | assert_eq!(Tokens::free_balance(DOT, &ALICE), ENDOWED_AMOUNT - 10); 78 | assert_eq!(Tokens::free_balance(BTC, &ALICE), ENDOWED_AMOUNT + 1); 79 | assert_eq!(Tokens::free_balance(DOT, &BOB), ENDOWED_AMOUNT + 10); 80 | assert_eq!(Tokens::free_balance(BTC, &BOB), ENDOWED_AMOUNT - 1); 81 | 82 | assert_eq!( 83 | events().as_slice(), 84 | [ 85 | Event::pallet_exchange(crate::Event::OrderCreated( 86 | 0, 87 | Order { 88 | base_currency_id: DOT, 89 | base_amount: 10, 90 | target_currency_id: BTC, 91 | target_amount: 1, 92 | owner: ALICE 93 | } 94 | )), 95 | Event::orml_currencies(orml_currencies::Event::Transferred(BTC, BOB, ALICE, 1)), 96 | Event::pallet_exchange(crate::Event::OrderTaken( 97 | BOB, 98 | 0, 99 | Order { 100 | base_currency_id: DOT, 101 | base_amount: 10, 102 | target_currency_id: BTC, 103 | target_amount: 1, 104 | owner: ALICE 105 | } 106 | )) 107 | ] 108 | ); 109 | }); 110 | } 111 | 112 | #[test] 113 | fn test_cancel_order() { 114 | new_test_ext().execute_with(|| { 115 | //id not exist 116 | assert_noop!( 117 | ExchangeModule::cancel_order(Origin::signed(ALICE), 0), 118 | Error::::InvalidOrderId 119 | ); 120 | 121 | //id exist, it is not owner 122 | assert_ok!(ExchangeModule::submit_order( 123 | Origin::signed(ALICE), 124 | DOT, 125 | 10, 126 | BTC, 127 | 1 128 | )); 129 | 130 | assert_noop!( 131 | ExchangeModule::cancel_order(Origin::signed(BOB), 0), 132 | Error::::NotOwner 133 | ); 134 | 135 | //id exist, is owner 136 | assert_ok!(ExchangeModule::cancel_order(Origin::signed(ALICE), 0)); 137 | 138 | assert_eq!( 139 | events().as_slice(), 140 | [ 141 | Event::pallet_exchange(crate::Event::OrderCreated( 142 | 0, 143 | Order { 144 | base_currency_id: DOT, 145 | base_amount: 10, 146 | target_currency_id: BTC, 147 | target_amount: 1, 148 | owner: ALICE 149 | } 150 | )), 151 | Event::pallet_exchange(crate::Event::OrderCancelled(0)) 152 | ] 153 | ); 154 | }); 155 | } 156 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Substrate DevHub '] 3 | edition = '2018' 4 | homepage = 'https://substrate.dev' 5 | license = 'Unlicense' 6 | name = 'node-template-runtime' 7 | repository = 'https://github.com/substrate-developer-hub/substrate-node-template/' 8 | version = '3.0.0' 9 | 10 | [package.metadata.docs.rs] 11 | targets = ['x86_64-unknown-linux-gnu'] 12 | 13 | [build-dependencies] 14 | substrate-wasm-builder = '4.0.0' 15 | 16 | [dependencies] 17 | codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0' } 18 | hex-literal = { optional = true, version = '0.3.1' } 19 | serde = { version = "1.0.119", optional = true, features = ["derive"] } 20 | 21 | # local dependencies 22 | pallet-exchange = { path = '../pallets/exchange', default-features = false, version = '3.0.0' } 23 | 24 | # Substrate dependencies 25 | frame-benchmarking = { default-features = false, optional = true, version = '3.0.0' } 26 | frame-executive = { default-features = false, version = '3.0.0' } 27 | frame-support = { default-features = false, version = '3.0.0' } 28 | frame-system = { default-features = false, version = '3.0.0' } 29 | frame-system-benchmarking = { default-features = false, optional = true, version = '3.0.0' } 30 | frame-system-rpc-runtime-api = { default-features = false, version = '3.0.0' } 31 | pallet-aura = { default-features = false, version = '3.0.0' } 32 | pallet-balances = { default-features = false, version = '3.0.0' } 33 | pallet-grandpa = { default-features = false, version = '3.0.0' } 34 | pallet-randomness-collective-flip = { default-features = false, version = '3.0.0' } 35 | pallet-sudo = { default-features = false, version = '3.0.0' } 36 | pallet-timestamp = { default-features = false, version = '3.0.0' } 37 | pallet-transaction-payment = { default-features = false, version = '3.0.0' } 38 | pallet-transaction-payment-rpc-runtime-api = { default-features = false, version = '3.0.0' } 39 | sp-api = { default-features = false, version = '3.0.0' } 40 | sp-block-builder = { default-features = false, version = '3.0.0' } 41 | sp-consensus-aura = { default-features = false, version = '0.9.0' } 42 | sp-core = { default-features = false, version = '3.0.0' } 43 | sp-inherents = { default-features = false, version = '3.0.0' } 44 | sp-offchain = { default-features = false, version = '3.0.0' } 45 | sp-runtime = { default-features = false, version = '3.0.0' } 46 | sp-session = { default-features = false, version = '3.0.0' } 47 | sp-std = { default-features = false, version = '3.0.0' } 48 | sp-transaction-pool = { default-features = false, version = '3.0.0' } 49 | sp-version = { default-features = false, version = '3.0.0' } 50 | 51 | orml-currencies = {default-features = false, version = '0.4.0'} 52 | orml-tokens = {default-features = false, version = '0.4.0'} 53 | orml-traits = {default-features = false, version = '0.4.0'} 54 | 55 | [features] 56 | default = ['std'] 57 | runtime-benchmarks = [ 58 | 'hex-literal', 59 | 'frame-benchmarking', 60 | 'frame-support/runtime-benchmarks', 61 | 'frame-system-benchmarking', 62 | 'frame-system/runtime-benchmarks', 63 | 'pallet-balances/runtime-benchmarks', 64 | 'pallet-timestamp/runtime-benchmarks', 65 | 'sp-runtime/runtime-benchmarks', 66 | ] 67 | std = [ 68 | 'codec/std', 69 | 'serde', 70 | 'frame-executive/std', 71 | 'frame-support/std', 72 | 'frame-system/std', 73 | 'frame-system-rpc-runtime-api/std', 74 | 'pallet-aura/std', 75 | 'pallet-balances/std', 76 | 'pallet-grandpa/std', 77 | 'pallet-randomness-collective-flip/std', 78 | 'pallet-sudo/std', 79 | 'pallet-timestamp/std', 80 | 'pallet-transaction-payment/std', 81 | 'pallet-transaction-payment-rpc-runtime-api/std', 82 | 'sp-api/std', 83 | 'sp-block-builder/std', 84 | 'sp-consensus-aura/std', 85 | 'sp-core/std', 86 | 'sp-inherents/std', 87 | 'sp-offchain/std', 88 | 'sp-runtime/std', 89 | 'sp-session/std', 90 | 'sp-std/std', 91 | 'sp-transaction-pool/std', 92 | 'sp-version/std', 93 | 'orml-currencies/std', 94 | 'orml-tokens/std', 95 | 'orml-traits/std', 96 | 'pallet-exchange/std', 97 | ] 98 | -------------------------------------------------------------------------------- /runtime/build.rs: -------------------------------------------------------------------------------- 1 | use substrate_wasm_builder::WasmBuilder; 2 | 3 | fn main() { 4 | WasmBuilder::new() 5 | .with_current_project() 6 | .import_memory() 7 | .export_heap_base() 8 | .build() 9 | } -------------------------------------------------------------------------------- /runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. 3 | #![recursion_limit="256"] 4 | 5 | // Make the WASM binary available. 6 | #[cfg(feature = "std")] 7 | include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); 8 | 9 | use codec::{Decode, Encode}; 10 | use pallet_grandpa::fg_primitives; 11 | use pallet_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; 12 | #[cfg(feature = "std")] 13 | use serde::{Deserialize, Serialize}; 14 | use sp_api::impl_runtime_apis; 15 | use sp_consensus_aura::sr25519::AuthorityId as AuraId; 16 | use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; 17 | use sp_runtime::traits::{ 18 | AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, Verify, 19 | }; 20 | use sp_runtime::{ 21 | create_runtime_str, generic, impl_opaque_keys, 22 | traits::Zero, 23 | transaction_validity::{TransactionSource, TransactionValidity}, 24 | ApplyExtrinsicResult, MultiSignature, RuntimeDebug, 25 | }; 26 | use sp_std::prelude::*; 27 | #[cfg(feature = "std")] 28 | use sp_version::NativeVersion; 29 | use sp_version::RuntimeVersion; 30 | 31 | use orml_currencies::BasicCurrencyAdapter; 32 | use orml_traits::parameter_type_with_key; 33 | 34 | // A few exports that help ease life for downstream crates. 35 | pub use frame_support::{ 36 | construct_runtime, parameter_types, 37 | traits::{KeyOwnerProofSystem, Randomness}, 38 | weights::{ 39 | constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, 40 | IdentityFee, Weight, 41 | }, 42 | StorageValue, 43 | }; 44 | pub use pallet_balances::Call as BalancesCall; 45 | pub use pallet_timestamp::Call as TimestampCall; 46 | use pallet_transaction_payment::CurrencyAdapter; 47 | #[cfg(any(feature = "std", test))] 48 | pub use sp_runtime::BuildStorage; 49 | pub use sp_runtime::{Perbill, Permill}; 50 | 51 | pub use pallet_exchange; 52 | 53 | /// An index to a block. 54 | pub type BlockNumber = u32; 55 | 56 | /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. 57 | pub type Signature = MultiSignature; 58 | 59 | /// Some way of identifying an account on the chain. We intentionally make it equivalent 60 | /// to the public key of our transaction signing scheme. 61 | pub type AccountId = <::Signer as IdentifyAccount>::AccountId; 62 | 63 | /// The type for looking up accounts. We don't expect more than 4 billion of them, but you 64 | /// never know... 65 | pub type AccountIndex = u32; 66 | 67 | /// Balance of an account. 68 | pub type Balance = u128; 69 | 70 | /// Index of a transaction in the chain. 71 | pub type Index = u32; 72 | 73 | /// A hash of some data used by the chain. 74 | pub type Hash = sp_core::H256; 75 | 76 | /// Digest item type. 77 | pub type DigestItem = generic::DigestItem; 78 | 79 | #[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord)] 80 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] 81 | pub enum CurrencyId { 82 | Native, 83 | DOT, 84 | KSM, 85 | BTC, 86 | } 87 | 88 | pub type Amount = i128; 89 | 90 | /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know 91 | /// the specifics of the runtime. They can then be made to be agnostic over specific formats 92 | /// of data like extrinsics, allowing for them to continue syncing the network through upgrades 93 | /// to even the core data structures. 94 | pub mod opaque { 95 | use super::*; 96 | 97 | pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; 98 | 99 | /// Opaque block header type. 100 | pub type Header = generic::Header; 101 | /// Opaque block type. 102 | pub type Block = generic::Block; 103 | /// Opaque block identifier type. 104 | pub type BlockId = generic::BlockId; 105 | 106 | impl_opaque_keys! { 107 | pub struct SessionKeys { 108 | pub aura: Aura, 109 | pub grandpa: Grandpa, 110 | } 111 | } 112 | } 113 | 114 | // To learn more about runtime versioning and what each of the following value means: 115 | // https://substrate.dev/docs/en/knowledgebase/runtime/upgrades#runtime-versioning 116 | pub const VERSION: RuntimeVersion = RuntimeVersion { 117 | spec_name: create_runtime_str!("node-template"), 118 | impl_name: create_runtime_str!("node-template"), 119 | authoring_version: 1, 120 | // The version of the runtime specification. A full node will not attempt to use its native 121 | // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, 122 | // `spec_version`, and `authoring_version` are the same between Wasm and native. 123 | // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use 124 | // the compatible custom types. 125 | spec_version: 100, 126 | impl_version: 1, 127 | apis: RUNTIME_API_VERSIONS, 128 | transaction_version: 1, 129 | }; 130 | 131 | /// This determines the average expected block time that we are targeting. 132 | /// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. 133 | /// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked 134 | /// up by `pallet_aura` to implement `fn slot_duration()`. 135 | /// 136 | /// Change this to adjust the block time. 137 | pub const MILLISECS_PER_BLOCK: u64 = 6000; 138 | 139 | pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; 140 | 141 | // Time is measured by number of blocks. 142 | pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); 143 | pub const HOURS: BlockNumber = MINUTES * 60; 144 | pub const DAYS: BlockNumber = HOURS * 24; 145 | 146 | /// The version information used to identify this runtime when compiled natively. 147 | #[cfg(feature = "std")] 148 | pub fn native_version() -> NativeVersion { 149 | NativeVersion { 150 | runtime_version: VERSION, 151 | can_author_with: Default::default(), 152 | } 153 | } 154 | 155 | const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); 156 | 157 | parameter_types! { 158 | pub const Version: RuntimeVersion = VERSION; 159 | pub const BlockHashCount: BlockNumber = 2400; 160 | /// We allow for 2 seconds of compute with a 6 second average block time. 161 | pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights 162 | ::with_sensible_defaults(2 * WEIGHT_PER_SECOND, NORMAL_DISPATCH_RATIO); 163 | pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength 164 | ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); 165 | pub const SS58Prefix: u8 = 42; 166 | } 167 | 168 | // Configure FRAME pallets to include in runtime. 169 | 170 | impl frame_system::Config for Runtime { 171 | /// The basic call filter to use in dispatchable. 172 | type BaseCallFilter = (); 173 | /// Block & extrinsics weights: base values and limits. 174 | type BlockWeights = BlockWeights; 175 | /// The maximum length of a block (in bytes). 176 | type BlockLength = BlockLength; 177 | /// The identifier used to distinguish between accounts. 178 | type AccountId = AccountId; 179 | /// The aggregated dispatch type that is available for extrinsics. 180 | type Call = Call; 181 | /// The lookup mechanism to get account ID from whatever is passed in dispatchers. 182 | type Lookup = AccountIdLookup; 183 | /// The index type for storing how many extrinsics an account has signed. 184 | type Index = Index; 185 | /// The index type for blocks. 186 | type BlockNumber = BlockNumber; 187 | /// The type for hashing blocks and tries. 188 | type Hash = Hash; 189 | /// The hashing algorithm used. 190 | type Hashing = BlakeTwo256; 191 | /// The header type. 192 | type Header = generic::Header; 193 | /// The ubiquitous event type. 194 | type Event = Event; 195 | /// The ubiquitous origin type. 196 | type Origin = Origin; 197 | /// Maximum number of block number to block hash mappings to keep (oldest pruned first). 198 | type BlockHashCount = BlockHashCount; 199 | /// The weight of database operations that the runtime can invoke. 200 | type DbWeight = RocksDbWeight; 201 | /// Version of the runtime. 202 | type Version = Version; 203 | /// Converts a module to the index of the module in `construct_runtime!`. 204 | /// 205 | /// This type is being generated by `construct_runtime!`. 206 | type PalletInfo = PalletInfo; 207 | /// What to do if a new account is created. 208 | type OnNewAccount = (); 209 | /// What to do if an account is fully reaped from the system. 210 | type OnKilledAccount = (); 211 | /// The data to be stored in an account. 212 | type AccountData = pallet_balances::AccountData; 213 | /// Weight information for the extrinsics of this pallet. 214 | type SystemWeightInfo = (); 215 | /// This is used as an identifier of the chain. 42 is the generic substrate prefix. 216 | type SS58Prefix = SS58Prefix; 217 | } 218 | 219 | impl pallet_aura::Config for Runtime { 220 | type AuthorityId = AuraId; 221 | } 222 | 223 | impl pallet_grandpa::Config for Runtime { 224 | type Event = Event; 225 | type Call = Call; 226 | 227 | type KeyOwnerProofSystem = (); 228 | 229 | type KeyOwnerProof = 230 | >::Proof; 231 | 232 | type KeyOwnerIdentification = >::IdentificationTuple; 236 | 237 | type HandleEquivocation = (); 238 | 239 | type WeightInfo = (); 240 | } 241 | 242 | parameter_types! { 243 | pub const MinimumPeriod: u64 = SLOT_DURATION / 2; 244 | } 245 | 246 | impl pallet_timestamp::Config for Runtime { 247 | /// A timestamp: milliseconds since the unix epoch. 248 | type Moment = u64; 249 | type OnTimestampSet = Aura; 250 | type MinimumPeriod = MinimumPeriod; 251 | type WeightInfo = (); 252 | } 253 | 254 | parameter_types! { 255 | pub const ExistentialDeposit: u128 = 500; 256 | pub const MaxLocks: u32 = 50; 257 | } 258 | 259 | impl pallet_balances::Config for Runtime { 260 | type MaxLocks = MaxLocks; 261 | /// The type for recording an account's balance. 262 | type Balance = Balance; 263 | /// The ubiquitous event type. 264 | type Event = Event; 265 | type DustRemoval = (); 266 | type ExistentialDeposit = ExistentialDeposit; 267 | type AccountStore = System; 268 | type WeightInfo = pallet_balances::weights::SubstrateWeight; 269 | } 270 | 271 | parameter_types! { 272 | pub const TransactionByteFee: Balance = 1; 273 | } 274 | 275 | impl pallet_transaction_payment::Config for Runtime { 276 | type OnChargeTransaction = CurrencyAdapter; 277 | type TransactionByteFee = TransactionByteFee; 278 | type WeightToFee = IdentityFee; 279 | type FeeMultiplierUpdate = (); 280 | } 281 | 282 | impl pallet_sudo::Config for Runtime { 283 | type Event = Event; 284 | type Call = Call; 285 | } 286 | 287 | impl orml_tokens::Config for Runtime { 288 | type Event = Event; 289 | type Balance = Balance; 290 | type Amount = Amount; 291 | type CurrencyId = CurrencyId; 292 | type WeightInfo = (); 293 | type ExistentialDeposits = ExistentialDeposits; 294 | type OnDust = (); 295 | } 296 | 297 | parameter_type_with_key! { 298 | pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { 299 | Zero::zero() 300 | }; 301 | } 302 | 303 | parameter_types! { 304 | pub const GetNativeCurrencyId: CurrencyId = CurrencyId::Native; 305 | } 306 | 307 | impl orml_currencies::Config for Runtime { 308 | type Event = Event; 309 | type MultiCurrency = Tokens; 310 | type NativeCurrency = BasicCurrencyAdapter; 311 | type GetNativeCurrencyId = GetNativeCurrencyId; 312 | type WeightInfo = (); 313 | } 314 | 315 | /// Configure the pallet-template in pallets/template. 316 | impl pallet_exchange::Config for Runtime { 317 | type Event = Event; 318 | type Currency = Currencies; 319 | type OrderId = u32; 320 | } 321 | 322 | // Create the runtime by composing the FRAME pallets that were previously configured. 323 | construct_runtime!( 324 | pub enum Runtime where 325 | Block = Block, 326 | NodeBlock = opaque::Block, 327 | UncheckedExtrinsic = UncheckedExtrinsic 328 | { 329 | System: frame_system::{Module, Call, Config, Storage, Event}, 330 | RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage}, 331 | Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent}, 332 | Aura: pallet_aura::{Module, Config}, 333 | Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event}, 334 | Balances: pallet_balances::{Module, Call, Storage, Config, Event}, 335 | TransactionPayment: pallet_transaction_payment::{Module, Storage}, 336 | Sudo: pallet_sudo::{Module, Call, Config, Storage, Event}, 337 | 338 | Currencies: orml_currencies::{Module, Call, Event}, 339 | Tokens: orml_tokens::{Module, Storage, Event, Config}, 340 | 341 | Exchange: pallet_exchange::{Module, Storage, Call, Event}, 342 | } 343 | ); 344 | 345 | /// The address format for describing accounts. 346 | pub type Address = sp_runtime::MultiAddress; 347 | /// Block header type as expected by this runtime. 348 | pub type Header = generic::Header; 349 | /// Block type as expected by this runtime. 350 | pub type Block = generic::Block; 351 | /// A Block signed with a Justification 352 | pub type SignedBlock = generic::SignedBlock; 353 | /// BlockId type as expected by this runtime. 354 | pub type BlockId = generic::BlockId; 355 | /// The SignedExtension to the basic transaction logic. 356 | pub type SignedExtra = ( 357 | frame_system::CheckSpecVersion, 358 | frame_system::CheckTxVersion, 359 | frame_system::CheckGenesis, 360 | frame_system::CheckEra, 361 | frame_system::CheckNonce, 362 | frame_system::CheckWeight, 363 | pallet_transaction_payment::ChargeTransactionPayment 364 | ); 365 | /// Unchecked extrinsic type as expected by this runtime. 366 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; 367 | /// Extrinsic type that has already been checked. 368 | pub type CheckedExtrinsic = generic::CheckedExtrinsic; 369 | /// Executive: handles dispatch to the various modules. 370 | pub type Executive = frame_executive::Executive< 371 | Runtime, 372 | Block, 373 | frame_system::ChainContext, 374 | Runtime, 375 | AllModules, 376 | >; 377 | 378 | impl_runtime_apis! { 379 | impl sp_api::Core for Runtime { 380 | fn version() -> RuntimeVersion { 381 | VERSION 382 | } 383 | 384 | fn execute_block(block: Block) { 385 | Executive::execute_block(block) 386 | } 387 | 388 | fn initialize_block(header: &::Header) { 389 | Executive::initialize_block(header) 390 | } 391 | } 392 | 393 | impl sp_api::Metadata for Runtime { 394 | fn metadata() -> OpaqueMetadata { 395 | Runtime::metadata().into() 396 | } 397 | } 398 | 399 | impl sp_block_builder::BlockBuilder for Runtime { 400 | fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { 401 | Executive::apply_extrinsic(extrinsic) 402 | } 403 | 404 | fn finalize_block() -> ::Header { 405 | Executive::finalize_block() 406 | } 407 | 408 | fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { 409 | data.create_extrinsics() 410 | } 411 | 412 | fn check_inherents( 413 | block: Block, 414 | data: sp_inherents::InherentData, 415 | ) -> sp_inherents::CheckInherentsResult { 416 | data.check_extrinsics(&block) 417 | } 418 | 419 | fn random_seed() -> ::Hash { 420 | RandomnessCollectiveFlip::random_seed() 421 | } 422 | } 423 | 424 | impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { 425 | fn validate_transaction( 426 | source: TransactionSource, 427 | tx: ::Extrinsic, 428 | ) -> TransactionValidity { 429 | Executive::validate_transaction(source, tx) 430 | } 431 | } 432 | 433 | impl sp_offchain::OffchainWorkerApi for Runtime { 434 | fn offchain_worker(header: &::Header) { 435 | Executive::offchain_worker(header) 436 | } 437 | } 438 | 439 | impl sp_consensus_aura::AuraApi for Runtime { 440 | fn slot_duration() -> u64 { 441 | Aura::slot_duration() 442 | } 443 | 444 | fn authorities() -> Vec { 445 | Aura::authorities() 446 | } 447 | } 448 | 449 | impl sp_session::SessionKeys for Runtime { 450 | fn generate_session_keys(seed: Option>) -> Vec { 451 | opaque::SessionKeys::generate(seed) 452 | } 453 | 454 | fn decode_session_keys( 455 | encoded: Vec, 456 | ) -> Option, KeyTypeId)>> { 457 | opaque::SessionKeys::decode_into_raw_public_keys(&encoded) 458 | } 459 | } 460 | 461 | impl fg_primitives::GrandpaApi for Runtime { 462 | fn grandpa_authorities() -> GrandpaAuthorityList { 463 | Grandpa::grandpa_authorities() 464 | } 465 | 466 | fn submit_report_equivocation_unsigned_extrinsic( 467 | _equivocation_proof: fg_primitives::EquivocationProof< 468 | ::Hash, 469 | NumberFor, 470 | >, 471 | _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, 472 | ) -> Option<()> { 473 | None 474 | } 475 | 476 | fn generate_key_ownership_proof( 477 | _set_id: fg_primitives::SetId, 478 | _authority_id: GrandpaId, 479 | ) -> Option { 480 | // NOTE: this is the only implementation possible since we've 481 | // defined our key owner proof type as a bottom type (i.e. a type 482 | // with no values). 483 | None 484 | } 485 | } 486 | 487 | impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { 488 | fn account_nonce(account: AccountId) -> Index { 489 | System::account_nonce(account) 490 | } 491 | } 492 | 493 | impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { 494 | fn query_info( 495 | uxt: ::Extrinsic, 496 | len: u32, 497 | ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { 498 | TransactionPayment::query_info(uxt, len) 499 | } 500 | fn query_fee_details( 501 | uxt: ::Extrinsic, 502 | len: u32, 503 | ) -> pallet_transaction_payment::FeeDetails { 504 | TransactionPayment::query_fee_details(uxt, len) 505 | } 506 | } 507 | 508 | #[cfg(feature = "runtime-benchmarks")] 509 | impl frame_benchmarking::Benchmark for Runtime { 510 | fn dispatch_benchmark( 511 | config: frame_benchmarking::BenchmarkConfig 512 | ) -> Result, sp_runtime::RuntimeString> { 513 | use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey}; 514 | 515 | use frame_system_benchmarking::Module as SystemBench; 516 | impl frame_system_benchmarking::Config for Runtime {} 517 | 518 | let whitelist: Vec = vec![ 519 | // Block Number 520 | hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac") 521 | .to_vec().into(), 522 | // Total Issuance 523 | hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80") 524 | .to_vec().into(), 525 | // Execution Phase 526 | hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a") 527 | .to_vec().into(), 528 | // Event Count 529 | hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850") 530 | .to_vec().into(), 531 | // System Events 532 | hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7") 533 | .to_vec().into(), 534 | ]; 535 | 536 | let mut batches = Vec::::new(); 537 | let params = (&config, &whitelist); 538 | 539 | add_benchmark!(params, batches, frame_system, SystemBench::); 540 | add_benchmark!(params, batches, pallet_balances, Balances); 541 | add_benchmark!(params, batches, pallet_timestamp, Timestamp); 542 | add_benchmark!(params, batches, pallet_exchange, Exchange); 543 | 544 | if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } 545 | Ok(batches) 546 | } 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /runtime/src/multiaddress.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | //! MultiAddress type is a wrapper for multiple downstream account formats. 19 | //! Copied from `sp_runtime::multiaddress`, and can be removed and replaced when 20 | //! updating to a newer version of Substrate. 21 | 22 | use codec::{Encode, Decode, Codec}; 23 | use sp_std::{vec::Vec, marker::PhantomData, fmt::Debug}; 24 | use sp_runtime::{RuntimeDebug, traits::{LookupError, StaticLookup}}; 25 | 26 | /// A multi-format address wrapper for on-chain accounts. 27 | #[derive(Encode, Decode, PartialEq, Eq, Clone, RuntimeDebug)] 28 | #[cfg_attr(feature = "std", derive(Hash))] 29 | pub enum MultiAddress { 30 | /// It's an account ID (pubkey). 31 | Id(AccountId), 32 | /// It's an account index. 33 | Index(#[codec(compact)] AccountIndex), 34 | /// It's some arbitrary raw bytes. 35 | Raw(Vec), 36 | /// It's a 32 byte representation. 37 | Address32([u8; 32]), 38 | /// Its a 20 byte representation. 39 | Address20([u8; 20]), 40 | } 41 | 42 | #[cfg(feature = "std")] 43 | impl std::fmt::Display for MultiAddress 44 | where 45 | AccountId: std::fmt::Debug, 46 | AccountIndex: std::fmt::Debug, 47 | { 48 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 49 | use sp_core::hexdisplay::HexDisplay; 50 | match self { 51 | MultiAddress::Raw(inner) => write!(f, "MultiAddress::Raw({})", HexDisplay::from(inner)), 52 | MultiAddress::Address32(inner) => write!(f, "MultiAddress::Address32({})", HexDisplay::from(inner)), 53 | MultiAddress::Address20(inner) => write!(f, "MultiAddress::Address20({})", HexDisplay::from(inner)), 54 | _ => write!(f, "{:?}", self), 55 | } 56 | } 57 | } 58 | 59 | impl From for MultiAddress { 60 | fn from(a: AccountId) -> Self { 61 | MultiAddress::Id(a) 62 | } 63 | } 64 | 65 | impl Default for MultiAddress { 66 | fn default() -> Self { 67 | MultiAddress::Id(Default::default()) 68 | } 69 | } 70 | 71 | /// A lookup implementation returning the `AccountId` from a `MultiAddress`. 72 | pub struct AccountIdLookup(PhantomData<(AccountId, AccountIndex)>); 73 | impl StaticLookup for AccountIdLookup 74 | where 75 | AccountId: Codec + Clone + PartialEq + Debug, 76 | AccountIndex: Codec + Clone + PartialEq + Debug, 77 | MultiAddress: Codec, 78 | { 79 | type Source = MultiAddress; 80 | type Target = AccountId; 81 | fn lookup(x: Self::Source) -> Result { 82 | match x { 83 | MultiAddress::Id(i) => Ok(i), 84 | _ => Err(LookupError), 85 | } 86 | } 87 | fn unlookup(x: Self::Target) -> Self::Source { 88 | MultiAddress::Id(x) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /scripts/docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Start Substrate node template ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | 9 | docker-compose down --remove-orphans 10 | docker-compose run --rm --service-ports dev $@ -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Initializing WASM build environment" 6 | 7 | if [ -z $CI_PROJECT_NAME ] ; then 8 | rustup update nightly 9 | rustup update stable 10 | fi 11 | 12 | rustup target add wasm32-unknown-unknown --toolchain nightly 13 | -------------------------------------------------------------------------------- /types.json: -------------------------------------------------------------------------------- 1 | { 2 | "CurrencyId": { 3 | "_enum": [ 4 | "Native", 5 | "DOT", 6 | "KSM", 7 | "BTC" 8 | ] 9 | }, 10 | "CurrencyIdOf": "CurrencyId", 11 | "Amount": "i128", 12 | "AmountOf": "Amount", 13 | "Order": { 14 | "base_currency_id": "CurrencyId", 15 | "base_amount": "Compact", 16 | "target_currency_id": "CurrencyId", 17 | "target_amount": "Compact", 18 | "owner": "AccountId" 19 | }, 20 | "OrderOf": "Order", 21 | "OrderId": "u32" 22 | } --------------------------------------------------------------------------------