├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── makefile ├── rpi ├── Dockerfile ├── README.md └── pi-build.sh ├── runtime ├── Cargo.toml ├── build.rs └── src │ └── lib.rs ├── scripts └── init.sh ├── src ├── chain_spec.rs ├── cli.rs ├── command.rs ├── main.rs └── service.rs ├── truffle ├── .gitignore ├── build │ └── contracts │ │ └── MyToken.json ├── contracts │ └── MyToken.sol ├── package-lock.json ├── package.json └── truffle-config.js ├── ui ├── .env ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── public │ ├── Substrate-Logo.png │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── AccountSelector.js │ ├── App.js │ ├── BlockNumber.js │ ├── EVMAccounts.js │ ├── EVMContracts.js │ ├── Metadata.js │ ├── NodeInfo.js │ ├── __tests__ │ │ └── App.js │ ├── config │ │ ├── common.json │ │ ├── development.json │ │ ├── index.js │ │ ├── production.json │ │ └── test.json │ ├── index.js │ └── substrate-lib │ │ ├── SubstrateContext.js │ │ ├── components │ │ ├── DeveloperConsole.js │ │ ├── TxButton.js │ │ └── index.js │ │ ├── index.js │ │ ├── useSubstrate.js │ │ └── utils.js └── yarn.lock └── utils ├── .gitignore ├── README.md ├── erc20-slot.js ├── evm-address.js ├── index.js ├── package-lock.json └── package.json /.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 -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.release] 2 | panic = 'unwind' 3 | 4 | [package] 5 | authors = ['Dan Forbes '] 6 | build = 'build.rs' 7 | edition = '2018' 8 | name = 'substrate-evm-template' 9 | version = '2.0.0' 10 | 11 | [build-dependencies] 12 | vergen = '3.0.4' 13 | 14 | [build-dependencies.build-script-utils] 15 | git = 'https://github.com/paritytech/substrate.git' 16 | package = 'substrate-build-script-utils' 17 | branch = 'master' 18 | version = '2.0.0' 19 | 20 | [dependencies] 21 | futures = '0.3.1' 22 | log = '0.4.8' 23 | parking_lot = '0.9.0' 24 | structopt = "0.3.8" 25 | tokio = '0.1.22' 26 | trie-root = '0.15.2' 27 | 28 | [dependencies.codec] 29 | package = 'parity-scale-codec' 30 | version = '1.0.0' 31 | 32 | [dependencies.ctrlc] 33 | features = ['termination'] 34 | version = '3.1.3' 35 | 36 | [dependencies.evm] 37 | default-features = false 38 | git = 'https://github.com/paritytech/substrate.git' 39 | package = 'pallet-evm' 40 | branch = 'master' 41 | version = '2.0.0' 42 | 43 | [dependencies.futures01] 44 | package = 'futures' 45 | version = '0.1.29' 46 | 47 | [dependencies.grandpa] 48 | git = 'https://github.com/paritytech/substrate.git' 49 | package = 'sc-finality-grandpa' 50 | branch = 'master' 51 | version = '0.8.0' 52 | 53 | [dependencies.grandpa-primitives] 54 | git = 'https://github.com/paritytech/substrate.git' 55 | package = 'sp-finality-grandpa' 56 | branch = 'master' 57 | version = '2.0.0' 58 | 59 | [dependencies.substrate-evm-runtime] 60 | path = 'runtime' 61 | version = '2.0.0' 62 | 63 | [dependencies.sc-basic-authorship] 64 | git = 'https://github.com/paritytech/substrate.git' 65 | branch = 'master' 66 | 67 | [dependencies.sc-cli] 68 | git = 'https://github.com/paritytech/substrate.git' 69 | branch = 'master' 70 | version = '0.8.0' 71 | 72 | [dependencies.sc-client] 73 | git = 'https://github.com/paritytech/substrate.git' 74 | branch = 'master' 75 | version = '0.8.0' 76 | 77 | [dependencies.sc-consensus-aura] 78 | git = 'https://github.com/paritytech/substrate.git' 79 | branch = 'master' 80 | version = '0.8' 81 | 82 | [dependencies.sc-executor] 83 | git = 'https://github.com/paritytech/substrate.git' 84 | branch = 'master' 85 | version = '0.8.0' 86 | 87 | [dependencies.sc-network] 88 | git = 'https://github.com/paritytech/substrate.git' 89 | branch = 'master' 90 | version = '0.8' 91 | 92 | [dependencies.sc-service] 93 | git = 'https://github.com/paritytech/substrate.git' 94 | branch = 'master' 95 | version = '0.8.0' 96 | 97 | [dependencies.sc-transaction-pool] 98 | git = 'https://github.com/paritytech/substrate.git' 99 | branch = 'master' 100 | version = '2.0.0' 101 | 102 | [dependencies.sp-consensus] 103 | git = 'https://github.com/paritytech/substrate.git' 104 | branch = 'master' 105 | version = '0.8' 106 | 107 | [dependencies.sp-consensus-aura] 108 | git = 'https://github.com/paritytech/substrate.git' 109 | branch = 'master' 110 | version = '0.8' 111 | 112 | [dependencies.sp-core] 113 | git = 'https://github.com/paritytech/substrate.git' 114 | branch = 'master' 115 | version = '2.0.0' 116 | 117 | [dependencies.sp-inherents] 118 | git = 'https://github.com/paritytech/substrate.git' 119 | branch = 'master' 120 | version = '2.0.0' 121 | 122 | [dependencies.sp-io] 123 | git = 'https://github.com/paritytech/substrate.git' 124 | branch = 'master' 125 | version = '2.0.0' 126 | 127 | [dependencies.sp-runtime] 128 | git = 'https://github.com/paritytech/substrate.git' 129 | branch = 'master' 130 | version = '2.0.0' 131 | 132 | [dependencies.sp-transaction-pool] 133 | git = 'https://github.com/paritytech/substrate.git' 134 | branch = 'master' 135 | version = '2.0.0' 136 | 137 | [workspace] 138 | members = ['runtime'] 139 | 140 | [[bin]] 141 | name = 'substrate-evm' 142 | path = 'src/main.rs' 143 | -------------------------------------------------------------------------------- /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 | [![Included in the Arctic Code Vault](https://img.shields.io/badge/Arctic%20Code%20Vault-blue)](https://archiveprogram.github.com/) 2 | 3 | # Substrate Node + EVM Pallet Template 4 | 5 | **This project has been archived.** Please refer to more up-to-date projects, such as 6 | [Parity's Frontier project](https://github.com/paritytech/frontier) and the fantastic work being 7 | done by the [Moonbeam Network](https://docs.moonbeam.network/). 8 | 9 | A [FRAME](https://substrate.dev/docs/en/next/conceptual/runtime/frame)-based 10 | [Substrate](https://substrate.dev/en/) node with the 11 | [EVM pallet](https://substrate.dev/docs/en/next/conceptual/runtime/frame#evm), ready for hacking 12 | :rocket: 13 | 14 | ## Upstream 15 | 16 | This project was forked from the 17 | [Substrate Node Template](https://github.com/substrate-developer-hub/substrate-node-template). 18 | 19 | ## Build & Run 20 | 21 | To build the chain, execute the following commands from the project root: 22 | 23 | ``` 24 | $ ./scripts/init.sh && cargo build --release 25 | ``` 26 | 27 | To execute the chain, run: 28 | 29 | ``` 30 | $ ./target/release/substrate-evm --dev 31 | ``` 32 | 33 | A [`makefile`](/makefile) is provided in order to document and encapsulate commands such as these. 34 | In this case, the above commands are associated with the `build-chain` and `start-chain` `make` 35 | targets. 36 | 37 | ## Genesis Configuration 38 | 39 | The development [chain spec](/src/chain_spec.rs) included with this project defines a genesis block 40 | that has been pre-configured with an EVM account for 41 | [Alice](https://substrate.dev/docs/en/next/development/tools/subkey#well-known-keys). When 42 | [a development chain is started](https://github.com/substrate-developer-hub/substrate-node-template#run), 43 | Alice's EVM account will be funded with a large amount of Ether (`U256::MAX`). The 44 | [Polkadot UI](https://substrate.dev/docs/en/next/development/front-end/polkadot-js#polkadot-js-apps) 45 | can be used to see the details of Alice's EVM account. In order to view an EVM account, use the 46 | `Developer` tab of the Polkadot UI `Settings` app to define the EVM `Account` type: 47 | 48 | ```json 49 | "Account": { 50 | "nonce": "U256", 51 | "balance": "U256" 52 | } 53 | ``` 54 | 55 | Use the `Chain State` app's `Storage` tab to query `evm > accounts` with Alice's EVM account ID 56 | (`0x57d213d0927ccc7596044c6ba013dd05522aacba`); the value that is returned should be: 57 | `{"nonce":0,"balance":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}`. 58 | 59 | > Further reading: 60 | > [EVM accounts](https://github.com/danforbes/danforbes/blob/master/writings/eth-dev.md#Accounts) 61 | 62 | Alice's EVM account ID was calculated using 63 | [a provided utility](/utils/README.md#--evm-address-address). 64 | 65 | ## Contract Deployment 66 | 67 | The [`truffle`](/truffle) directory contains a [Truffle](https://www.trufflesuite.com/truffle) 68 | project that defines [an ERC-20 token](/truffle/contracts/MyToken.sol). For convenience, this 69 | repository also contains 70 | [the compiled bytecode of this token contract](/truffle/build/contracts/MyToken.json#L259), which 71 | can be used to deploy it to the Substrate blockchain. 72 | 73 | > Further reading: 74 | > [the ERC-20 token standard](https://github.com/danforbes/danforbes/blob/master/writings/eth-dev.md#EIP-20-ERC-20-Token-Standard) 75 | 76 | Use the Polkadot UI `Extrinsics` app to deploy the contract from Alice's account (submit the 77 | extrinsic as a signed transaction) using `evm > create` with the following parameters: 78 | 79 | ``` 80 | init: 81 | value: 0 82 | gas_limit: 4294967295 83 | gas_price: 1 84 | ``` 85 | 86 | The values for `gas_limit` and `gas_price` were chosen for convenience and have little inherent or 87 | special meaning. 88 | 89 | While the extrinsic is processing, open the browser console and take note of the output. Once the 90 | extrinsic has finalized, the EVM pallet will fire a `Created` event with an `address` field that 91 | provides the address of the newly-created contract. In this case, however, it is trivial to 92 | [calculate this value](https://ethereum.stackexchange.com/a/46960): 93 | `0x11650d764feb44f78810ef08700c2284f7e81dcb`. That is because EVM contract account IDs are 94 | determined solely by the ID and nonce of the contract creator's account and, in this case, both of 95 | those values are well-known (`0x57d213d0927ccc7596044c6ba013dd05522aacba` and `0x0`, respectively). 96 | 97 | Use the `Chain State` app to view the EVM accounts for Alice and the newly-created contract; notice 98 | that Alice's `nonce` has been incremented to `1` and her `balance` has decreased. Next, query 99 | `evm > accountCodes` for both Alice's and the contract's account IDs; notice that Alice's account 100 | code is empty and the contract's is equal to the bytecode of the Solidity contract. 101 | 102 | ## Contract Storage 103 | 104 | The ERC-20 contract that was deployed inherits from 105 | [the OpenZeppelin ERC-20 implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) 106 | and extends its capabilities by adding 107 | [a constructor that mints a maximum amount of tokens to the contract creator](/truffle/contracts/MyToken.sol#L8). 108 | Use the `Chain State` app to query `evm > accountStorage` and view the value associated with Alice's 109 | account in the `_balances` map of the ERC-20 contract; use the ERC-20 contract address 110 | (`0x11650d764feb44f78810ef08700c2284f7e81dcb`) as the first parameter and the storage slot to read 111 | as the second parameter (`0xa7473b24b6fd8e15602cfb2f15c6a2e2770a692290d0c5097b77dd334132b7ce`). The 112 | value that is returned should be 113 | `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`. 114 | 115 | The storage slot was calculated using 116 | [a provided utility](/utils/README.md#--erc20-slot-slot-address). 117 | 118 | > Further reading: 119 | > [EVM layout of state variables in storage](https://solidity.readthedocs.io/en/v0.6.2/miscellaneous.html#layout-of-state-variables-in-storage) 120 | 121 | ## Contract Usage 122 | 123 | Use the `Extrinsics` app to invoke the `transfer(address, uint256)` function on the ERC-20 contract 124 | with `evm > call` and transfer some of the ERC-20 tokens from Alice to Bob. 125 | 126 | ``` 127 | target: 0x11650d764feb44f78810ef08700c2284f7e81dcb 128 | input: 0xa9059cbb0000000000000000000000008bc395119f39603402d0c85bc9eda5dfc5ae216000000000000000000000000000000000000000000000000000000000000000dd 129 | value: 0 130 | gas_limit: 4294967295 131 | gas_price: 1 132 | ``` 133 | 134 | The value of the `input` parameter is an EVM ABI-encoded function call that was calculated using 135 | [the Remix web IDE](http://remix.ethereum.org); it consists of a function selector (`0xa9059cbb`) 136 | and the arguments to be used for the function invocation. In this case, the arguments correspond to 137 | Bob's EVM account ID (`0x8bc395119f39603402d0c85bc9eda5dfc5ae2160`) and the number of tokens to be 138 | transferred (`0xdd`, or 221 in hex). 139 | 140 | > Further reading: 141 | > [the EVM ABI specification](https://solidity.readthedocs.io/en/v0.6.2/abi-spec.html) 142 | 143 | After the extrinsic has finalized, use the `Chain State` app to query `evm > accountStorage` to see 144 | the ERC-20 balances for both Alice and Bob. 145 | 146 | ## Polkadot UI 147 | 148 | This project contains [a Polkadot UI](/ui) with some custom components for the EVM pallet. 149 | 150 | ## Raspberry Pi 151 | 152 | See [the `rpi` directory](/rpi) for information about cross-compiling a Substrate node for execution 153 | on a Raspberry Pi. 154 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use vergen::{ConstantsFlags, generate_cargo_keys}; 2 | 3 | const ERROR_MSG: &str = "Failed to generate metadata files"; 4 | 5 | fn main() { 6 | generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG); 7 | 8 | build_script_utils::rerun_if_git_head_changed(); 9 | } 10 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | init-wasm : 2 | ./scripts/init.sh 3 | 4 | build-chain : init-wasm 5 | cargo build --release 6 | 7 | purge-chain : 8 | ./target/release/substrate-evm purge-chain --dev 9 | 10 | clean-chain : purge-chain 11 | rm -fr ./target 12 | 13 | start-chain : 14 | ./target/release/substrate-evm --dev 15 | 16 | build-ui : 17 | cd ./ui && yarn 18 | 19 | clean-ui : 20 | rm -fr ./ui/node_modules 21 | 22 | start-ui: 23 | cd ui && yarn start 24 | 25 | podman-build : 26 | podman build --tag subpi:latest ./rpi/ 27 | 28 | pi-build : podman-build 29 | ./rpi/pi-build.sh 30 | -------------------------------------------------------------------------------- /rpi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:latest 2 | 3 | RUN dpkg --add-architecture armhf && \ 4 | apt-get update && apt-get upgrade -y && \ 5 | apt-get install -y aptitude && \ 6 | aptitude install -y \ 7 | gcc-arm-linux-gnueabihf \ 8 | libgtk-3-dev:armhf \ 9 | pkg-config \ 10 | libasound2-dev:armhf \ 11 | g++-arm-linux-gnueabihf \ 12 | cmake \ 13 | libssl-dev \ 14 | git \ 15 | clang \ 16 | libclang-dev \ 17 | libssl-dev:armhf \ 18 | libsdl2-dev:armhf 19 | 20 | # Install nightly with w32-u-u and add arvm7 to stable 21 | RUN rustup target add armv7-unknown-linux-gnueabihf 22 | RUN rustup install nightly && \ 23 | rustup target add wasm32-unknown-unknown --toolchain nightly &&\ 24 | cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force 25 | 26 | ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER arm-linux-gnueabihf-gcc 27 | ENV PKG_CONFIG_ALLOW_CROSS 1 28 | ENV PKG_CONFIG_PATH /usr/lib/arm-linux-gnueabihf/pkgconfig/ 29 | 30 | RUN useradd rust --user-group --create-home --shell /bin/bash --groups sudo 31 | WORKDIR /home/rust/src 32 | -------------------------------------------------------------------------------- /rpi/README.md: -------------------------------------------------------------------------------- 1 | # Cross-Compile for Raspberry Pi 2 | 3 | The Dockerfile and build script in this directory can be used to create an executable that will run on a [Raspberry Pi](https://www.raspberrypi.org). These tools rely on [`podman`](https://podman.io/) and it must be installed in order to use them. To build the executable, run `make pi-build` in the project root and copy `substrate-evm` from the `./target/armv7-unknown-linux-gnueabihf/release` directory to the Raspberry Pi. Execute the binary from the Pi's terminal to start a blockchain that implements the Ethereum Virtual Machine :rocket: 4 | 5 | ## External Polkadot UI 6 | 7 | In order to use an external Polkadot UI with a Substrate node, the node must be started with the `--ws-external`/`--unsafe-ws-external` and `--rpc-cors all` flags. Note that these flags are not safe for production and should only be used for development purposes. 8 | 9 | ## Acknowledgments 10 | 11 | Thanks to [Sergei Pepyakin](https://github.com/pepyakin) for providing [the tools](https://gist.github.com/pepyakin/2ff227c2d837a2eacd8d3879d5e0c94f) needed to make all this work :pray: 12 | -------------------------------------------------------------------------------- /rpi/pi-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | podman run --rm -it \ 3 | --env BINDGEN_EXTRA_CLANG_ARGS="--sysroot=/usr/arm-linux-gnueabihf -D__ARM_PCS_VFP -mfpu=vfp -mfloat-abi=hard" \ 4 | -v "$(pwd)":/home/rust/src subpi:latest \ 5 | cargo build --target=armv7-unknown-linux-gnueabihf --release 6 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [dependencies.aura] 2 | default-features = false 3 | git = 'https://github.com/paritytech/substrate.git' 4 | package = 'pallet-aura' 5 | branch = 'master' 6 | version = '2.0.0' 7 | 8 | [dependencies.balances] 9 | default-features = false 10 | git = 'https://github.com/paritytech/substrate.git' 11 | package = 'pallet-balances' 12 | branch = 'master' 13 | version = '2.0.0' 14 | 15 | [dependencies.codec] 16 | default-features = false 17 | features = ['derive'] 18 | package = 'parity-scale-codec' 19 | version = '1.0.0' 20 | 21 | [dependencies.evm] 22 | default-features = false 23 | git = 'https://github.com/paritytech/substrate.git' 24 | package = 'pallet-evm' 25 | branch = 'master' 26 | version = '2.0.0' 27 | 28 | [dependencies.frame-executive] 29 | default-features = false 30 | git = 'https://github.com/paritytech/substrate.git' 31 | branch = 'master' 32 | version = '2.0.0' 33 | 34 | [dependencies.frame-support] 35 | default-features = false 36 | git = 'https://github.com/paritytech/substrate.git' 37 | branch = 'master' 38 | version = '2.0.0' 39 | 40 | [dependencies.grandpa] 41 | default-features = false 42 | git = 'https://github.com/paritytech/substrate.git' 43 | package = 'pallet-grandpa' 44 | branch = 'master' 45 | version = '2.0.0' 46 | 47 | [dependencies.indices] 48 | default-features = false 49 | git = 'https://github.com/paritytech/substrate.git' 50 | package = 'pallet-indices' 51 | branch = 'master' 52 | version = '2.0.0' 53 | 54 | [dependencies.randomness-collective-flip] 55 | default-features = false 56 | git = 'https://github.com/paritytech/substrate.git' 57 | package = 'pallet-randomness-collective-flip' 58 | branch = 'master' 59 | version = '2.0.0' 60 | 61 | [dependencies.safe-mix] 62 | default-features = false 63 | version = '1.0.0' 64 | 65 | [dependencies.serde] 66 | features = ['derive'] 67 | optional = true 68 | version = '1.0.101' 69 | 70 | [dependencies.sp-api] 71 | default-features = false 72 | git = 'https://github.com/paritytech/substrate.git' 73 | branch = 'master' 74 | version = '2.0.0' 75 | 76 | [dependencies.sp-block-builder] 77 | default-features = false 78 | git = 'https://github.com/paritytech/substrate.git' 79 | branch = 'master' 80 | 81 | [dependencies.sp-consensus-aura] 82 | default-features = false 83 | git = 'https://github.com/paritytech/substrate.git' 84 | branch = 'master' 85 | version = '0.8' 86 | 87 | [dependencies.sp-core] 88 | default-features = false 89 | git = 'https://github.com/paritytech/substrate.git' 90 | branch = 'master' 91 | version = '2.0.0' 92 | 93 | [dependencies.sp-inherents] 94 | default-features = false 95 | git = 'https://github.com/paritytech/substrate.git' 96 | branch = 'master' 97 | 98 | [dependencies.sp-io] 99 | default-features = false 100 | git = 'https://github.com/paritytech/substrate.git' 101 | branch = 'master' 102 | version = '2.0.0' 103 | 104 | [dependencies.sp-offchain] 105 | default-features = false 106 | git = 'https://github.com/paritytech/substrate.git' 107 | branch = 'master' 108 | version = '2.0.0' 109 | 110 | [dependencies.sp-runtime] 111 | default-features = false 112 | git = 'https://github.com/paritytech/substrate.git' 113 | branch = 'master' 114 | version = '2.0.0' 115 | 116 | [dependencies.sp-session] 117 | default-features = false 118 | git = 'https://github.com/paritytech/substrate.git' 119 | branch = 'master' 120 | version = '2.0.0' 121 | 122 | [dependencies.sp-std] 123 | default-features = false 124 | git = 'https://github.com/paritytech/substrate.git' 125 | branch = 'master' 126 | version = '2.0.0' 127 | 128 | [dependencies.sp-transaction-pool] 129 | default-features = false 130 | git = 'https://github.com/paritytech/substrate.git' 131 | branch = 'master' 132 | version = '2.0.0' 133 | 134 | [dependencies.sp-version] 135 | default-features = false 136 | git = 'https://github.com/paritytech/substrate.git' 137 | branch = 'master' 138 | version = '2.0.0' 139 | 140 | [dependencies.sudo] 141 | default-features = false 142 | git = 'https://github.com/paritytech/substrate.git' 143 | package = 'pallet-sudo' 144 | branch = 'master' 145 | version = '2.0.0' 146 | 147 | [dependencies.system] 148 | default-features = false 149 | git = 'https://github.com/paritytech/substrate.git' 150 | package = 'frame-system' 151 | branch = 'master' 152 | version = '2.0.0' 153 | 154 | [dependencies.timestamp] 155 | default-features = false 156 | git = 'https://github.com/paritytech/substrate.git' 157 | package = 'pallet-timestamp' 158 | branch = 'master' 159 | version = '2.0.0' 160 | 161 | [dependencies.transaction-payment] 162 | default-features = false 163 | git = 'https://github.com/paritytech/substrate.git' 164 | package = 'pallet-transaction-payment' 165 | branch = 'master' 166 | version = '2.0.0' 167 | 168 | [features] 169 | default = ['std'] 170 | std = [ 171 | 'aura/std', 172 | 'balances/std', 173 | 'codec/std', 174 | 'evm/std', 175 | 'frame-executive/std', 176 | 'frame-support/std', 177 | 'grandpa/std', 178 | 'indices/std', 179 | 'randomness-collective-flip/std', 180 | 'safe-mix/std', 181 | 'serde', 182 | 'sp-api/std', 183 | 'sp-block-builder/std', 184 | 'sp-consensus-aura/std', 185 | 'sp-core/std', 186 | 'sp-inherents/std', 187 | 'sp-io/std', 188 | 'sp-offchain/std', 189 | 'sp-runtime/std', 190 | 'sp-session/std', 191 | 'sp-std/std', 192 | 'sp-transaction-pool/std', 193 | 'sp-version/std', 194 | 'sudo/std', 195 | 'system/std', 196 | 'timestamp/std', 197 | 'transaction-payment/std', 198 | ] 199 | 200 | [package] 201 | authors = ['Dan Forbes '] 202 | edition = '2018' 203 | name = 'substrate-evm-runtime' 204 | version = '2.0.0' 205 | [build-dependencies.wasm-builder-runner] 206 | git = 'https://github.com/paritytech/substrate.git' 207 | package = 'substrate-wasm-builder-runner' 208 | branch = 'master' 209 | version = '1.0.4' 210 | -------------------------------------------------------------------------------- /runtime/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Parity Technologies (UK) Ltd. 2 | // This file is part of Substrate. 3 | 4 | // Substrate is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | 9 | // Substrate is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | 14 | // You should have received a copy of the GNU General Public License 15 | // along with Substrate. If not, see . 16 | 17 | use wasm_builder_runner::WasmBuilder; 18 | 19 | fn main() { 20 | WasmBuilder::new() 21 | .with_current_project() 22 | .with_wasm_builder_from_crates("1.0.9") 23 | .export_heap_base() 24 | .import_memory() 25 | .build() 26 | } 27 | -------------------------------------------------------------------------------- /runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. 5 | #![recursion_limit="256"] 6 | 7 | // Make the WASM binary available. 8 | #[cfg(feature = "std")] 9 | include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); 10 | 11 | use sp_std::prelude::*; 12 | use sp_core::{Blake2Hasher, OpaqueMetadata, U256}; 13 | use sp_runtime::{ 14 | ApplyExtrinsicResult, transaction_validity::TransactionValidity, generic, create_runtime_str, 15 | impl_opaque_keys, MultiSignature 16 | }; 17 | use sp_runtime::traits::{ 18 | BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto, IdentifyAccount 19 | }; 20 | use sp_api::impl_runtime_apis; 21 | use sp_consensus_aura::sr25519::AuthorityId as AuraId; 22 | use grandpa::AuthorityList as GrandpaAuthorityList; 23 | use grandpa::fg_primitives; 24 | use sp_version::RuntimeVersion; 25 | #[cfg(feature = "std")] 26 | use sp_version::NativeVersion; 27 | 28 | use evm::{FeeCalculator, HashTruncateConvertAccountId}; 29 | 30 | // A few exports that help ease life for downstream crates. 31 | #[cfg(any(feature = "std", test))] 32 | pub use sp_runtime::BuildStorage; 33 | pub use timestamp::Call as TimestampCall; 34 | pub use balances::Call as BalancesCall; 35 | pub use sp_runtime::{Permill, Perbill}; 36 | pub use frame_support::{ 37 | StorageValue, construct_runtime, parameter_types, 38 | traits::Randomness, 39 | weights::Weight, 40 | }; 41 | pub use evm::Account as EVMAccount; 42 | 43 | /// An index to a block. 44 | pub type BlockNumber = u32; 45 | 46 | /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. 47 | pub type Signature = MultiSignature; 48 | 49 | /// Some way of identifying an account on the chain. We intentionally make it equivalent 50 | /// to the public key of our transaction signing scheme. 51 | pub type AccountId = <::Signer as IdentifyAccount>::AccountId; 52 | 53 | /// The type for looking up accounts. We don't expect more than 4 billion of them, but you 54 | /// never know... 55 | pub type AccountIndex = u32; 56 | 57 | /// Balance of an account. 58 | pub type Balance = u128; 59 | 60 | /// Index of a transaction in the chain. 61 | pub type Index = u32; 62 | 63 | /// A hash of some data used by the chain. 64 | pub type Hash = sp_core::H256; 65 | 66 | /// Digest item type. 67 | pub type DigestItem = generic::DigestItem; 68 | 69 | // EVM structs 70 | pub struct FixedGasPrice; 71 | 72 | /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know 73 | /// the specifics of the runtime. They can then be made to be agnostic over specific formats 74 | /// of data like extrinsics, allowing for them to continue syncing the network through upgrades 75 | /// to even the core datastructures. 76 | pub mod opaque { 77 | use super::*; 78 | 79 | pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; 80 | 81 | /// Opaque block header type. 82 | pub type Header = generic::Header; 83 | /// Opaque block type. 84 | pub type Block = generic::Block; 85 | /// Opaque block identifier type. 86 | pub type BlockId = generic::BlockId; 87 | 88 | impl_opaque_keys! { 89 | pub struct SessionKeys { 90 | pub aura: Aura, 91 | pub grandpa: Grandpa, 92 | } 93 | } 94 | } 95 | 96 | /// This runtime version. 97 | pub const VERSION: RuntimeVersion = RuntimeVersion { 98 | spec_name: create_runtime_str!("substrate-evm"), 99 | impl_name: create_runtime_str!("substrate-evm"), 100 | authoring_version: 1, 101 | spec_version: 1, 102 | impl_version: 1, 103 | apis: RUNTIME_API_VERSIONS, 104 | }; 105 | 106 | pub const MILLISECS_PER_BLOCK: u64 = 6000; 107 | 108 | pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; 109 | 110 | // These time units are defined in number of blocks. 111 | pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); 112 | pub const HOURS: BlockNumber = MINUTES * 60; 113 | pub const DAYS: BlockNumber = HOURS * 24; 114 | 115 | /// The version infromation used to identify this runtime when compiled natively. 116 | #[cfg(feature = "std")] 117 | pub fn native_version() -> NativeVersion { 118 | NativeVersion { 119 | runtime_version: VERSION, 120 | can_author_with: Default::default(), 121 | } 122 | } 123 | 124 | parameter_types! { 125 | pub const BlockHashCount: BlockNumber = 250; 126 | pub const MaximumBlockWeight: Weight = 1_000_000; 127 | pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); 128 | pub const MaximumBlockLength: u32 = 5 * 1024 * 1024; 129 | pub const Version: RuntimeVersion = VERSION; 130 | } 131 | 132 | impl system::Trait for Runtime { 133 | /// The identifier used to distinguish between accounts. 134 | type AccountId = AccountId; 135 | /// The aggregated dispatch type that is available for extrinsics. 136 | type Call = Call; 137 | /// The lookup mechanism to get account ID from whatever is passed in dispatchers. 138 | type Lookup = Indices; 139 | /// The index type for storing how many extrinsics an account has signed. 140 | type Index = Index; 141 | /// The index type for blocks. 142 | type BlockNumber = BlockNumber; 143 | /// The type for hashing blocks and tries. 144 | type Hash = Hash; 145 | /// The hashing algorithm used. 146 | type Hashing = BlakeTwo256; 147 | /// The header type. 148 | type Header = generic::Header; 149 | /// The ubiquitous event type. 150 | type Event = Event; 151 | /// The ubiquitous origin type. 152 | type Origin = Origin; 153 | /// Maximum number of block number to block hash mappings to keep (oldest pruned first). 154 | type BlockHashCount = BlockHashCount; 155 | /// Maximum weight of each block. 156 | type MaximumBlockWeight = MaximumBlockWeight; 157 | /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. 158 | type MaximumBlockLength = MaximumBlockLength; 159 | /// Portion of the block weight that is available to all normal transactions. 160 | type AvailableBlockRatio = AvailableBlockRatio; 161 | /// Version of the runtime. 162 | type Version = Version; 163 | /// Converts a module to the index of the module in `construct_runtime!`. 164 | /// 165 | /// This type is being generated by `construct_runtime!`. 166 | type ModuleToIndex = ModuleToIndex; 167 | } 168 | 169 | impl aura::Trait for Runtime { 170 | type AuthorityId = AuraId; 171 | } 172 | 173 | impl grandpa::Trait for Runtime { 174 | type Event = Event; 175 | } 176 | 177 | impl indices::Trait for Runtime { 178 | /// The type for recording indexing into the account enumeration. If this ever overflows, there 179 | /// will be problems! 180 | type AccountIndex = AccountIndex; 181 | /// Use the standard means of resolving an index hint from an id. 182 | type ResolveHint = indices::SimpleResolveHint; 183 | /// Determine whether an account is dead. 184 | type IsDeadAccount = Balances; 185 | /// The ubiquitous event type. 186 | type Event = Event; 187 | } 188 | 189 | parameter_types! { 190 | pub const MinimumPeriod: u64 = SLOT_DURATION / 2; 191 | } 192 | 193 | impl timestamp::Trait for Runtime { 194 | /// A timestamp: milliseconds since the unix epoch. 195 | type Moment = u64; 196 | type OnTimestampSet = Aura; 197 | type MinimumPeriod = MinimumPeriod; 198 | } 199 | 200 | parameter_types! { 201 | pub const ExistentialDeposit: u128 = 500; 202 | pub const TransferFee: u128 = 0; 203 | pub const CreationFee: u128 = 0; 204 | } 205 | 206 | impl balances::Trait for Runtime { 207 | /// The type for recording an account's balance. 208 | type Balance = Balance; 209 | /// What to do if an account's free balance gets zeroed. 210 | type OnReapAccount = (); 211 | /// What to do if a new account is created. 212 | type OnNewAccount = Indices; 213 | /// The ubiquitous event type. 214 | type Event = Event; 215 | type DustRemoval = (); 216 | type TransferPayment = (); 217 | type ExistentialDeposit = ExistentialDeposit; 218 | type CreationFee = CreationFee; 219 | } 220 | 221 | parameter_types! { 222 | pub const TransactionBaseFee: Balance = 0; 223 | pub const TransactionByteFee: Balance = 1; 224 | } 225 | 226 | impl transaction_payment::Trait for Runtime { 227 | type Currency = balances::Module; 228 | type OnTransactionPayment = (); 229 | type TransactionBaseFee = TransactionBaseFee; 230 | type TransactionByteFee = TransactionByteFee; 231 | type WeightToFee = ConvertInto; 232 | type FeeMultiplierUpdate = (); 233 | } 234 | 235 | impl sudo::Trait for Runtime { 236 | type Event = Event; 237 | type Proposal = Call; 238 | } 239 | 240 | impl FeeCalculator for FixedGasPrice { 241 | fn min_gas_price() -> U256 { 242 | // Gas price is always one token per gas. 243 | 1.into() 244 | } 245 | } 246 | 247 | impl evm::Trait for Runtime { 248 | type FeeCalculator = FixedGasPrice; 249 | type ConvertAccountId = HashTruncateConvertAccountId; 250 | type Currency = Balances; 251 | type Event = Event; 252 | type Precompiles = (); // We can use () here because paint_evm provides an 253 | // `impl Precompiles for ()`` 254 | // block that always returns none (line 75) 255 | } 256 | 257 | construct_runtime!( 258 | pub enum Runtime where 259 | Block = Block, 260 | NodeBlock = opaque::Block, 261 | UncheckedExtrinsic = UncheckedExtrinsic 262 | { 263 | System: system::{Module, Call, Storage, Config, Event}, 264 | Timestamp: timestamp::{Module, Call, Storage, Inherent}, 265 | Aura: aura::{Module, Config, Inherent(Timestamp)}, 266 | Grandpa: grandpa::{Module, Call, Storage, Config, Event}, 267 | Indices: indices, 268 | Balances: balances, 269 | TransactionPayment: transaction_payment::{Module, Storage}, 270 | Sudo: sudo, 271 | RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage}, 272 | EVM: evm::{Module, Config, Call, Storage, Event}, 273 | } 274 | ); 275 | 276 | /// The address format for describing accounts. 277 | pub type Address = ::Source; 278 | /// Block header type as expected by this runtime. 279 | pub type Header = generic::Header; 280 | /// Block type as expected by this runtime. 281 | pub type Block = generic::Block; 282 | /// A Block signed with a Justification 283 | pub type SignedBlock = generic::SignedBlock; 284 | /// BlockId type as expected by this runtime. 285 | pub type BlockId = generic::BlockId; 286 | /// The SignedExtension to the basic transaction logic. 287 | pub type SignedExtra = ( 288 | system::CheckVersion, 289 | system::CheckGenesis, 290 | system::CheckEra, 291 | system::CheckNonce, 292 | system::CheckWeight, 293 | transaction_payment::ChargeTransactionPayment 294 | ); 295 | /// Unchecked extrinsic type as expected by this runtime. 296 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; 297 | /// Extrinsic type that has already been checked. 298 | pub type CheckedExtrinsic = generic::CheckedExtrinsic; 299 | /// Executive: handles dispatch to the various modules. 300 | pub type Executive = frame_executive::Executive, Runtime, AllModules>; 301 | 302 | impl_runtime_apis! { 303 | impl sp_api::Core for Runtime { 304 | fn version() -> RuntimeVersion { 305 | VERSION 306 | } 307 | 308 | fn execute_block(block: Block) { 309 | Executive::execute_block(block) 310 | } 311 | 312 | fn initialize_block(header: &::Header) { 313 | Executive::initialize_block(header) 314 | } 315 | } 316 | 317 | impl sp_api::Metadata for Runtime { 318 | fn metadata() -> OpaqueMetadata { 319 | Runtime::metadata().into() 320 | } 321 | } 322 | 323 | impl sp_block_builder::BlockBuilder for Runtime { 324 | fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { 325 | Executive::apply_extrinsic(extrinsic) 326 | } 327 | 328 | fn finalize_block() -> ::Header { 329 | Executive::finalize_block() 330 | } 331 | 332 | fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { 333 | data.create_extrinsics() 334 | } 335 | 336 | fn check_inherents( 337 | block: Block, 338 | data: sp_inherents::InherentData, 339 | ) -> sp_inherents::CheckInherentsResult { 340 | data.check_extrinsics(&block) 341 | } 342 | 343 | fn random_seed() -> ::Hash { 344 | RandomnessCollectiveFlip::random_seed() 345 | } 346 | } 347 | 348 | impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { 349 | fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity { 350 | Executive::validate_transaction(tx) 351 | } 352 | } 353 | 354 | impl sp_offchain::OffchainWorkerApi for Runtime { 355 | fn offchain_worker(header: &::Header) { 356 | Executive::offchain_worker(header) 357 | } 358 | } 359 | 360 | impl sp_consensus_aura::AuraApi for Runtime { 361 | fn slot_duration() -> u64 { 362 | Aura::slot_duration() 363 | } 364 | 365 | fn authorities() -> Vec { 366 | Aura::authorities() 367 | } 368 | } 369 | 370 | impl sp_session::SessionKeys for Runtime { 371 | fn generate_session_keys(seed: Option>) -> Vec { 372 | opaque::SessionKeys::generate(seed) 373 | } 374 | 375 | fn decode_session_keys( 376 | encoded: Vec, 377 | ) -> Option, sp_core::crypto::KeyTypeId)>> { 378 | opaque::SessionKeys::decode_into_raw_public_keys(&encoded) 379 | } 380 | } 381 | 382 | impl fg_primitives::GrandpaApi for Runtime { 383 | fn grandpa_authorities() -> GrandpaAuthorityList { 384 | Grandpa::grandpa_authorities() 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/chain_spec.rs: -------------------------------------------------------------------------------- 1 | use sp_core::{Blake2Hasher, Pair, Public, sr25519, U256}; 2 | use substrate_evm_runtime::{ 3 | AccountId, AuraConfig, BalancesConfig, EVMAccount, EVMConfig, GenesisConfig, GrandpaConfig, 4 | SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, Signature 5 | }; 6 | use sp_consensus_aura::sr25519::{AuthorityId as AuraId}; 7 | use grandpa_primitives::{AuthorityId as GrandpaId}; 8 | use sc_service; 9 | use sp_runtime::traits::{Verify, IdentifyAccount}; 10 | 11 | use evm::{ConvertAccountId, HashTruncateConvertAccountId}; 12 | 13 | // Note this is the URL for the telemetry server 14 | //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; 15 | 16 | /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. 17 | pub type ChainSpec = sc_service::ChainSpec; 18 | 19 | /// The chain specification option. This is expected to come in from the CLI and 20 | /// is little more than one of a number of alternatives which can easily be converted 21 | /// from a string (`--chain=...`) into a `ChainSpec`. 22 | #[derive(Clone, Debug)] 23 | pub enum Alternative { 24 | /// Whatever the current runtime is, with just Alice as an auth. 25 | Development, 26 | /// Whatever the current runtime is, with simple Alice/Bob auths. 27 | LocalTestnet, 28 | } 29 | 30 | /// Helper function to generate a crypto pair from seed 31 | pub fn get_from_seed(seed: &str) -> ::Public { 32 | TPublic::Pair::from_string(&format!("//{}", seed), None) 33 | .expect("static values are valid; qed") 34 | .public() 35 | } 36 | 37 | type AccountPublic = ::Signer; 38 | 39 | /// Helper function to generate an account ID from seed 40 | pub fn get_account_id_from_seed(seed: &str) -> AccountId where 41 | AccountPublic: From<::Public> 42 | { 43 | AccountPublic::from(get_from_seed::(seed)).into_account() 44 | } 45 | 46 | /// Helper function to generate an authority key for Aura 47 | pub fn get_authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { 48 | ( 49 | get_from_seed::(s), 50 | get_from_seed::(s), 51 | ) 52 | } 53 | 54 | impl Alternative { 55 | /// Get an actual chain config from one of the alternatives. 56 | pub(crate) fn load(self) -> Result { 57 | Ok(match self { 58 | Alternative::Development => ChainSpec::from_genesis( 59 | "Development", 60 | "dev", 61 | || testnet_genesis( 62 | vec![ 63 | get_authority_keys_from_seed("Alice"), 64 | ], 65 | get_account_id_from_seed::("Alice"), 66 | vec![ 67 | get_account_id_from_seed::("Alice"), 68 | get_account_id_from_seed::("Bob"), 69 | get_account_id_from_seed::("Alice//stash"), 70 | get_account_id_from_seed::("Bob//stash"), 71 | ], 72 | true, 73 | ), 74 | vec![], 75 | None, 76 | None, 77 | None, 78 | None 79 | ), 80 | Alternative::LocalTestnet => ChainSpec::from_genesis( 81 | "Local Testnet", 82 | "local_testnet", 83 | || testnet_genesis( 84 | vec![ 85 | get_authority_keys_from_seed("Alice"), 86 | get_authority_keys_from_seed("Bob"), 87 | ], 88 | get_account_id_from_seed::("Alice"), 89 | vec![ 90 | get_account_id_from_seed::("Alice"), 91 | get_account_id_from_seed::("Bob"), 92 | get_account_id_from_seed::("Charlie"), 93 | get_account_id_from_seed::("Dave"), 94 | get_account_id_from_seed::("Eve"), 95 | get_account_id_from_seed::("Ferdie"), 96 | get_account_id_from_seed::("Alice//stash"), 97 | get_account_id_from_seed::("Bob//stash"), 98 | get_account_id_from_seed::("Charlie//stash"), 99 | get_account_id_from_seed::("Dave//stash"), 100 | get_account_id_from_seed::("Eve//stash"), 101 | get_account_id_from_seed::("Ferdie//stash"), 102 | ], 103 | true, 104 | ), 105 | vec![], 106 | None, 107 | None, 108 | None, 109 | None 110 | ), 111 | }) 112 | } 113 | 114 | pub(crate) fn from(s: &str) -> Option { 115 | match s { 116 | "dev" => Some(Alternative::Development), 117 | "" | "local" => Some(Alternative::LocalTestnet), 118 | _ => None, 119 | } 120 | } 121 | } 122 | 123 | fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>, 124 | root_key: AccountId, 125 | endowed_accounts: Vec, 126 | _enable_println: bool) -> GenesisConfig { 127 | let alice_account_id = get_account_id_from_seed::("Alice"); 128 | let alice_evm_account_id = HashTruncateConvertAccountId::::convert_account_id(&alice_account_id); 129 | 130 | GenesisConfig { 131 | system: Some(SystemConfig { 132 | code: WASM_BINARY.to_vec(), 133 | changes_trie_config: Default::default(), 134 | }), 135 | indices: Some(IndicesConfig { 136 | ids: endowed_accounts.clone(), 137 | }), 138 | balances: Some(BalancesConfig { 139 | balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), 140 | }), 141 | sudo: Some(SudoConfig { 142 | key: root_key, 143 | }), 144 | aura: Some(AuraConfig { 145 | authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), 146 | }), 147 | grandpa: Some(GrandpaConfig { 148 | authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), 149 | }), 150 | evm: Some(EVMConfig { 151 | accounts: vec![( 152 | alice_evm_account_id, 153 | EVMAccount { 154 | nonce: 0.into(), 155 | balance: U256::MAX, 156 | })], 157 | }), 158 | } 159 | } 160 | 161 | pub fn load_spec(id: &str) -> Result, String> { 162 | Ok(match Alternative::from(id) { 163 | Some(spec) => Some(spec.load()?), 164 | None => None, 165 | }) 166 | } 167 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use sc_cli::{RunCmd, Subcommand}; 2 | use structopt::StructOpt; 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 | -------------------------------------------------------------------------------- /src/command.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 Parity Technologies (UK) Ltd. 2 | // This file is part of Substrate. 3 | 4 | // Substrate is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | 9 | // Substrate is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | 14 | // You should have received a copy of the GNU General Public License 15 | // along with Substrate. If not, see . 16 | 17 | use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair}; 18 | use sc_cli::{VersionInfo, error}; 19 | use crate::service; 20 | use crate::chain_spec; 21 | use crate::cli::Cli; 22 | 23 | /// Parse and run command line arguments 24 | pub fn run(version: VersionInfo) -> error::Result<()> { 25 | let opt = sc_cli::from_args::(&version); 26 | 27 | let config = sc_service::Configuration::new(&version); 28 | 29 | match opt.subcommand { 30 | Some(subcommand) => sc_cli::run_subcommand( 31 | config, 32 | subcommand, 33 | chain_spec::load_spec, 34 | |config: _| Ok(new_full_start!(config).0), 35 | &version, 36 | ), 37 | None => sc_cli::run( 38 | config, 39 | opt.run, 40 | service::new_light, 41 | service::new_full, 42 | chain_spec::load_spec, 43 | &version, 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Substrate Node Template CLI library. 3 | #![warn(missing_docs)] 4 | 5 | mod chain_spec; 6 | #[macro_use] 7 | mod service; 8 | mod cli; 9 | mod command; 10 | 11 | pub use sc_cli::{VersionInfo, error}; 12 | 13 | fn main() -> Result<(), error::Error> { 14 | let version = VersionInfo { 15 | name: "Substrate Node", 16 | commit: env!("VERGEN_SHA_SHORT"), 17 | version: env!("CARGO_PKG_VERSION"), 18 | executable_name: "substrate-evm", 19 | author: "Anonymous", 20 | description: "Substrate + EVM Template Node", 21 | support_url: "support.anonymous.an", 22 | copyright_start_year: 2017, 23 | }; 24 | 25 | command::run(version) 26 | } 27 | -------------------------------------------------------------------------------- /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::LongestChain; 6 | use substrate_evm_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi}; 7 | use sc_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder}; 8 | use sp_inherents::InherentDataProviders; 9 | use sc_network::{construct_simple_protocol}; 10 | use sc_executor::native_executor_instance; 11 | pub use sc_executor::NativeExecutor; 12 | use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair}; 13 | use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; 14 | 15 | // Our native executor instance. 16 | native_executor_instance!( 17 | pub Executor, 18 | substrate_evm_runtime::api::dispatch, 19 | substrate_evm_runtime::native_version, 20 | ); 21 | 22 | construct_simple_protocol! { 23 | /// Demo protocol attachment for substrate. 24 | pub struct NodeProtocol where Block = Block { } 25 | } 26 | 27 | /// Starts a `ServiceBuilder` for a full service. 28 | /// 29 | /// Use this macro if you don't actually need the full service, but just the builder in order to 30 | /// be able to perform chain operations. 31 | macro_rules! new_full_start { 32 | ($config:expr) => {{ 33 | let mut import_setup = None; 34 | let inherent_data_providers = sp_inherents::InherentDataProviders::new(); 35 | 36 | let builder = sc_service::ServiceBuilder::new_full::< 37 | substrate_evm_runtime::opaque::Block, substrate_evm_runtime::RuntimeApi, crate::service::Executor 38 | >($config)? 39 | .with_select_chain(|_config, backend| { 40 | Ok(sc_client::LongestChain::new(backend.clone())) 41 | })? 42 | .with_transaction_pool(|config, client, _fetcher| { 43 | let pool_api = sc_transaction_pool::FullChainApi::new(client.clone()); 44 | let pool = sc_transaction_pool::BasicPool::new(config, std::sync::Arc::new(pool_api)); 45 | Ok(pool) 46 | })? 47 | .with_import_queue(|_config, client, mut select_chain, transaction_pool| { 48 | let select_chain = select_chain.take() 49 | .ok_or_else(|| sc_service::Error::SelectChainRequired)?; 50 | 51 | let (grandpa_block_import, grandpa_link) = 52 | grandpa::block_import::<_, _, _, substrate_evm_runtime::RuntimeApi, _>( 53 | client.clone(), &*client, select_chain 54 | )?; 55 | 56 | let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new( 57 | grandpa_block_import.clone(), client.clone(), 58 | ); 59 | 60 | let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _>( 61 | sc_consensus_aura::SlotDuration::get_or_compute(&*client)?, 62 | aura_block_import, 63 | Some(Box::new(grandpa_block_import.clone())), 64 | None, 65 | client, 66 | inherent_data_providers.clone(), 67 | Some(transaction_pool), 68 | )?; 69 | 70 | import_setup = Some((grandpa_block_import, grandpa_link)); 71 | 72 | Ok(import_queue) 73 | })?; 74 | 75 | (builder, import_setup, inherent_data_providers) 76 | }} 77 | } 78 | 79 | /// Builds a new service for a full client. 80 | pub fn new_full(config: Configuration) 81 | -> Result 82 | { 83 | let is_authority = config.roles.is_authority(); 84 | let force_authoring = config.force_authoring; 85 | let name = config.name.clone(); 86 | let disable_grandpa = config.disable_grandpa; 87 | 88 | // sentry nodes announce themselves as authorities to the network 89 | // and should run the same protocols authorities do, but it should 90 | // never actively participate in any consensus process. 91 | let participates_in_consensus = is_authority && !config.sentry_mode; 92 | 93 | let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config); 94 | 95 | let (block_import, grandpa_link) = 96 | import_setup.take() 97 | .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); 98 | 99 | let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))? 100 | .with_finality_proof_provider(|client, backend| 101 | Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _) 102 | )? 103 | .build()?; 104 | 105 | if participates_in_consensus { 106 | let proposer = sc_basic_authorship::ProposerFactory { 107 | client: service.client(), 108 | transaction_pool: service.transaction_pool(), 109 | }; 110 | 111 | let client = service.client(); 112 | let select_chain = service.select_chain() 113 | .ok_or(ServiceError::SelectChainRequired)?; 114 | 115 | let can_author_with = 116 | sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()); 117 | 118 | let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _>( 119 | sc_consensus_aura::SlotDuration::get_or_compute(&*client)?, 120 | client, 121 | select_chain, 122 | block_import, 123 | proposer, 124 | service.network(), 125 | inherent_data_providers.clone(), 126 | force_authoring, 127 | service.keystore(), 128 | can_author_with, 129 | )?; 130 | 131 | // the AURA authoring task is considered essential, i.e. if it 132 | // fails we take down the service with it. 133 | service.spawn_essential_task("aura", aura); 134 | } 135 | 136 | // if the node isn't actively participating in consensus then it doesn't 137 | // need a keystore, regardless of which protocol we use below. 138 | let keystore = if participates_in_consensus { 139 | Some(service.keystore()) 140 | } else { 141 | None 142 | }; 143 | 144 | let grandpa_config = grandpa::Config { 145 | // FIXME #1578 make this available through chainspec 146 | gossip_duration: Duration::from_millis(333), 147 | justification_period: 512, 148 | name: Some(name), 149 | observer_enabled: true, 150 | keystore, 151 | is_authority, 152 | }; 153 | 154 | match (is_authority, disable_grandpa) { 155 | (false, false) => { 156 | // start the lightweight GRANDPA observer 157 | service.spawn_task("grandpa-observer", grandpa::run_grandpa_observer( 158 | grandpa_config, 159 | grandpa_link, 160 | service.network(), 161 | service.on_exit(), 162 | service.spawn_task_handle(), 163 | )?); 164 | }, 165 | (true, false) => { 166 | // start the full GRANDPA voter 167 | let voter_config = grandpa::GrandpaParams { 168 | config: grandpa_config, 169 | link: grandpa_link, 170 | network: service.network(), 171 | inherent_data_providers: inherent_data_providers.clone(), 172 | on_exit: service.on_exit(), 173 | telemetry_on_connect: Some(service.telemetry_on_connect_stream()), 174 | voting_rule: grandpa::VotingRulesBuilder::default().build(), 175 | executor: service.spawn_task_handle(), 176 | }; 177 | 178 | // the GRANDPA voter task is considered infallible, i.e. 179 | // if it fails we take down the service with it. 180 | service.spawn_essential_task("grandpa", grandpa::run_grandpa_voter(voter_config)?); 181 | }, 182 | (_, true) => { 183 | grandpa::setup_disabled_grandpa( 184 | service.client(), 185 | &inherent_data_providers, 186 | service.network(), 187 | )?; 188 | }, 189 | } 190 | 191 | Ok(service) 192 | } 193 | 194 | /// Builds a new service for a light client. 195 | pub fn new_light(config: Configuration) 196 | -> Result 197 | { 198 | let inherent_data_providers = InherentDataProviders::new(); 199 | 200 | ServiceBuilder::new_light::(config)? 201 | .with_select_chain(|_config, backend| { 202 | Ok(LongestChain::new(backend.clone())) 203 | })? 204 | .with_transaction_pool(|config, client, fetcher| { 205 | let fetcher = fetcher 206 | .ok_or_else(|| "Trying to start light transaction pool without active fetcher")?; 207 | 208 | let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone()); 209 | let pool = sc_transaction_pool::BasicPool::with_revalidation_type( 210 | config, Arc::new(pool_api), sc_transaction_pool::RevalidationType::Light, 211 | ); 212 | Ok(pool) 213 | })? 214 | .with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| { 215 | let fetch_checker = fetcher 216 | .map(|fetcher| fetcher.checker().clone()) 217 | .ok_or_else(|| "Trying to start light import queue without active fetch checker")?; 218 | let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>( 219 | client.clone(), backend, &*client.clone(), Arc::new(fetch_checker), 220 | )?; 221 | let finality_proof_import = grandpa_block_import.clone(); 222 | let finality_proof_request_builder = 223 | finality_proof_import.create_finality_proof_request_builder(); 224 | 225 | let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, ()>( 226 | sc_consensus_aura::SlotDuration::get_or_compute(&*client)?, 227 | grandpa_block_import, 228 | None, 229 | Some(Box::new(finality_proof_import)), 230 | client, 231 | inherent_data_providers.clone(), 232 | None, 233 | )?; 234 | 235 | Ok((import_queue, finality_proof_request_builder)) 236 | })? 237 | .with_network_protocol(|_| Ok(NodeProtocol::new()))? 238 | .with_finality_proof_provider(|client, backend| 239 | Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _) 240 | )? 241 | .build() 242 | } 243 | -------------------------------------------------------------------------------- /truffle/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /truffle/build/contracts/MyToken.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "MyToken", 3 | "abi": [ 4 | { 5 | "inputs": [], 6 | "payable": false, 7 | "stateMutability": "nonpayable", 8 | "type": "constructor" 9 | }, 10 | { 11 | "anonymous": false, 12 | "inputs": [ 13 | { 14 | "indexed": true, 15 | "internalType": "address", 16 | "name": "owner", 17 | "type": "address" 18 | }, 19 | { 20 | "indexed": true, 21 | "internalType": "address", 22 | "name": "spender", 23 | "type": "address" 24 | }, 25 | { 26 | "indexed": false, 27 | "internalType": "uint256", 28 | "name": "value", 29 | "type": "uint256" 30 | } 31 | ], 32 | "name": "Approval", 33 | "type": "event" 34 | }, 35 | { 36 | "anonymous": false, 37 | "inputs": [ 38 | { 39 | "indexed": true, 40 | "internalType": "address", 41 | "name": "from", 42 | "type": "address" 43 | }, 44 | { 45 | "indexed": true, 46 | "internalType": "address", 47 | "name": "to", 48 | "type": "address" 49 | }, 50 | { 51 | "indexed": false, 52 | "internalType": "uint256", 53 | "name": "value", 54 | "type": "uint256" 55 | } 56 | ], 57 | "name": "Transfer", 58 | "type": "event" 59 | }, 60 | { 61 | "constant": true, 62 | "inputs": [ 63 | { 64 | "internalType": "address", 65 | "name": "owner", 66 | "type": "address" 67 | }, 68 | { 69 | "internalType": "address", 70 | "name": "spender", 71 | "type": "address" 72 | } 73 | ], 74 | "name": "allowance", 75 | "outputs": [ 76 | { 77 | "internalType": "uint256", 78 | "name": "", 79 | "type": "uint256" 80 | } 81 | ], 82 | "payable": false, 83 | "stateMutability": "view", 84 | "type": "function" 85 | }, 86 | { 87 | "constant": false, 88 | "inputs": [ 89 | { 90 | "internalType": "address", 91 | "name": "spender", 92 | "type": "address" 93 | }, 94 | { 95 | "internalType": "uint256", 96 | "name": "amount", 97 | "type": "uint256" 98 | } 99 | ], 100 | "name": "approve", 101 | "outputs": [ 102 | { 103 | "internalType": "bool", 104 | "name": "", 105 | "type": "bool" 106 | } 107 | ], 108 | "payable": false, 109 | "stateMutability": "nonpayable", 110 | "type": "function" 111 | }, 112 | { 113 | "constant": true, 114 | "inputs": [ 115 | { 116 | "internalType": "address", 117 | "name": "account", 118 | "type": "address" 119 | } 120 | ], 121 | "name": "balanceOf", 122 | "outputs": [ 123 | { 124 | "internalType": "uint256", 125 | "name": "", 126 | "type": "uint256" 127 | } 128 | ], 129 | "payable": false, 130 | "stateMutability": "view", 131 | "type": "function" 132 | }, 133 | { 134 | "constant": false, 135 | "inputs": [ 136 | { 137 | "internalType": "address", 138 | "name": "spender", 139 | "type": "address" 140 | }, 141 | { 142 | "internalType": "uint256", 143 | "name": "subtractedValue", 144 | "type": "uint256" 145 | } 146 | ], 147 | "name": "decreaseAllowance", 148 | "outputs": [ 149 | { 150 | "internalType": "bool", 151 | "name": "", 152 | "type": "bool" 153 | } 154 | ], 155 | "payable": false, 156 | "stateMutability": "nonpayable", 157 | "type": "function" 158 | }, 159 | { 160 | "constant": false, 161 | "inputs": [ 162 | { 163 | "internalType": "address", 164 | "name": "spender", 165 | "type": "address" 166 | }, 167 | { 168 | "internalType": "uint256", 169 | "name": "addedValue", 170 | "type": "uint256" 171 | } 172 | ], 173 | "name": "increaseAllowance", 174 | "outputs": [ 175 | { 176 | "internalType": "bool", 177 | "name": "", 178 | "type": "bool" 179 | } 180 | ], 181 | "payable": false, 182 | "stateMutability": "nonpayable", 183 | "type": "function" 184 | }, 185 | { 186 | "constant": true, 187 | "inputs": [], 188 | "name": "totalSupply", 189 | "outputs": [ 190 | { 191 | "internalType": "uint256", 192 | "name": "", 193 | "type": "uint256" 194 | } 195 | ], 196 | "payable": false, 197 | "stateMutability": "view", 198 | "type": "function" 199 | }, 200 | { 201 | "constant": false, 202 | "inputs": [ 203 | { 204 | "internalType": "address", 205 | "name": "recipient", 206 | "type": "address" 207 | }, 208 | { 209 | "internalType": "uint256", 210 | "name": "amount", 211 | "type": "uint256" 212 | } 213 | ], 214 | "name": "transfer", 215 | "outputs": [ 216 | { 217 | "internalType": "bool", 218 | "name": "", 219 | "type": "bool" 220 | } 221 | ], 222 | "payable": false, 223 | "stateMutability": "nonpayable", 224 | "type": "function" 225 | }, 226 | { 227 | "constant": false, 228 | "inputs": [ 229 | { 230 | "internalType": "address", 231 | "name": "sender", 232 | "type": "address" 233 | }, 234 | { 235 | "internalType": "address", 236 | "name": "recipient", 237 | "type": "address" 238 | }, 239 | { 240 | "internalType": "uint256", 241 | "name": "amount", 242 | "type": "uint256" 243 | } 244 | ], 245 | "name": "transferFrom", 246 | "outputs": [ 247 | { 248 | "internalType": "bool", 249 | "name": "", 250 | "type": "bool" 251 | } 252 | ], 253 | "payable": false, 254 | "stateMutability": "nonpayable", 255 | "type": "function" 256 | } 257 | ], 258 | "metadata": "{\"compiler\":{\"version\":\"0.5.16+commit.9c3226ce\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{\"allowance(address,address)\":{\"details\":\"See {IERC20-allowance}.\"},\"approve(address,uint256)\":{\"details\":\"See {IERC20-approve}. * Requirements: * - `spender` cannot be the zero address.\"},\"balanceOf(address)\":{\"details\":\"See {IERC20-balanceOf}.\"},\"decreaseAllowance(address,uint256)\":{\"details\":\"Atomically decreases the allowance granted to `spender` by the caller. * This is an alternative to {approve} that can be used as a mitigation for problems described in {IERC20-approve}. * Emits an {Approval} event indicating the updated allowance. * Requirements: * - `spender` cannot be the zero address. - `spender` must have allowance for the caller of at least `subtractedValue`.\"},\"increaseAllowance(address,uint256)\":{\"details\":\"Atomically increases the allowance granted to `spender` by the caller. * This is an alternative to {approve} that can be used as a mitigation for problems described in {IERC20-approve}. * Emits an {Approval} event indicating the updated allowance. * Requirements: * - `spender` cannot be the zero address.\"},\"totalSupply()\":{\"details\":\"See {IERC20-totalSupply}.\"},\"transfer(address,uint256)\":{\"details\":\"See {IERC20-transfer}. * Requirements: * - `recipient` cannot be the zero address. - the caller must have a balance of at least `amount`.\"},\"transferFrom(address,address,uint256)\":{\"details\":\"See {IERC20-transferFrom}. * Emits an {Approval} event indicating the updated allowance. This is not required by the EIP. See the note at the beginning of {ERC20}; * Requirements: - `sender` and `recipient` cannot be the zero address. - `sender` must have a balance of at least `amount`. - the caller must have allowance for `sender`'s tokens of at least `amount`.\"}}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/home/dan/Code/Substrate/substrate-evm/contracts/contracts/MyToken.sol\":\"MyToken\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/dan/Code/Substrate/substrate-evm/contracts/contracts/MyToken.sol\":{\"keccak256\":\"0xe8a813e1dd98694d07ee458be5ffe924b8e1f41d2cf16ee682127755af1ddde1\",\"urls\":[\"bzz-raw://c8222b05c871d5b48ed808fd1a85e6c3b1cdf97e6632216d12c960d550fb8452\",\"dweb:/ipfs/QmRcQ2id9Ydg1VdZdea5Mo7mvYMihjH4y7paERBFVdxGBv\"]},\"@openzeppelin/contracts/GSN/Context.sol\":{\"keccak256\":\"0x90a3995645af7562d84b9d69363ffa5ae7217714ab61e951bf7bc450f40e4061\",\"urls\":[\"bzz-raw://216ef9d6b614db4eb46970b4e84903f2534a45572dd30a79f0041f1a5830f436\",\"dweb:/ipfs/QmNPrJ4MWKUAWzKXpUqeyKRUfosaoANZAqXgvepdrCwZAG\"]},\"@openzeppelin/contracts/math/SafeMath.sol\":{\"keccak256\":\"0x640b6dee7a4b830bdfd52b5031a07fc2b12209f5b2e29e5d364a7d37f69d8076\",\"urls\":[\"bzz-raw://31113152e1ddb78fe7a4197f247591ca894e93f916867beb708d8e747b6cc74f\",\"dweb:/ipfs/QmbZaJyXdpsYGykVhHH9qpVGQg9DGCxE2QufbCUy3daTgq\"]},\"@openzeppelin/contracts/token/ERC20/ERC20.sol\":{\"keccak256\":\"0xb15af804e2bc97db51e4e103f13de9fe13f87e6b835d7a88c897966c0e58506e\",\"urls\":[\"bzz-raw://1e8cff8437557fc915a3bed968fcd8f2df9809599e665ef69c2c9ce628548055\",\"dweb:/ipfs/QmP5spYP8vs2jvLF8zNrXUbqB79hMsoEvMHiLcBxerWKcm\"]},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0xe5bb0f57cff3e299f360052ba50f1ea0fff046df2be070b6943e0e3c3fdad8a9\",\"urls\":[\"bzz-raw://59fd025151435da35faa8093a5c7a17de02de9d08ad27275c5cdf05050820d91\",\"dweb:/ipfs/QmQMvwEcPhoRXzbXyrdoeRtvLoifUW9Qh7Luho7bmUPRkc\"]}},\"version\":1}", 259 | "bytecode": "0x608060405234801561001057600080fd5b50610041337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61004660201b60201c565b610291565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156100e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f45524332303a206d696e7420746f20746865207a65726f20616464726573730081525060200191505060405180910390fd5b6101028160025461020960201b610c7c1790919060201c565b60028190555061015d816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461020960201b610c7c1790919060201c565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b600080828401905083811015610287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b610e3a806102a06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806370a082311161005b57806370a08231146101fd578063a457c2d714610255578063a9059cbb146102bb578063dd62ed3e1461032157610088565b8063095ea7b31461008d57806318160ddd146100f357806323b872dd146101115780633950935114610197575b600080fd5b6100d9600480360360408110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610399565b604051808215151515815260200191505060405180910390f35b6100fb6103b7565b6040518082815260200191505060405180910390f35b61017d6004803603606081101561012757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103c1565b604051808215151515815260200191505060405180910390f35b6101e3600480360360408110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b604051808215151515815260200191505060405180910390f35b61023f6004803603602081101561021357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054d565b6040518082815260200191505060405180910390f35b6102a16004803603604081101561026b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610595565b604051808215151515815260200191505060405180910390f35b610307600480360360408110156102d157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610662565b604051808215151515815260200191505060405180910390f35b6103836004803603604081101561033757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610680565b6040518082815260200191505060405180910390f35b60006103ad6103a6610707565b848461070f565b6001905092915050565b6000600254905090565b60006103ce848484610906565b61048f846103da610707565b61048a85604051806060016040528060288152602001610d7060289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610440610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b600190509392505050565b60006105436104a7610707565b8461053e85600160006104b8610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b61070f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106586105a2610707565b8461065385604051806060016040528060258152602001610de160259139600160006105cc610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b6001905092915050565b600061067661066f610707565b8484610906565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610795576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180610dbd6024913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561081b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610d286022913960400191505060405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180610d986025913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610d056023913960400191505060405180910390fd5b610a7d81604051806060016040528060268152602001610d4a602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b10816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b6000838311158290610c69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610c2e578082015181840152602081019050610c13565b50505050905090810190601f168015610c5b5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080828401905083811015610cfa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b809150509291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa265627a7a72315820c7a5ffabf642bda14700b2de42f8c57b36621af020441df825de45fd2b3e1c5c64736f6c63430005100032", 260 | "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100885760003560e01c806370a082311161005b57806370a08231146101fd578063a457c2d714610255578063a9059cbb146102bb578063dd62ed3e1461032157610088565b8063095ea7b31461008d57806318160ddd146100f357806323b872dd146101115780633950935114610197575b600080fd5b6100d9600480360360408110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610399565b604051808215151515815260200191505060405180910390f35b6100fb6103b7565b6040518082815260200191505060405180910390f35b61017d6004803603606081101561012757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103c1565b604051808215151515815260200191505060405180910390f35b6101e3600480360360408110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b604051808215151515815260200191505060405180910390f35b61023f6004803603602081101561021357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054d565b6040518082815260200191505060405180910390f35b6102a16004803603604081101561026b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610595565b604051808215151515815260200191505060405180910390f35b610307600480360360408110156102d157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610662565b604051808215151515815260200191505060405180910390f35b6103836004803603604081101561033757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610680565b6040518082815260200191505060405180910390f35b60006103ad6103a6610707565b848461070f565b6001905092915050565b6000600254905090565b60006103ce848484610906565b61048f846103da610707565b61048a85604051806060016040528060288152602001610d7060289139600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610440610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b600190509392505050565b60006105436104a7610707565b8461053e85600160006104b8610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b61070f565b6001905092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106586105a2610707565b8461065385604051806060016040528060258152602001610de160259139600160006105cc610707565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b61070f565b6001905092915050565b600061067661066f610707565b8484610906565b6001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610795576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180610dbd6024913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561081b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610d286022913960400191505060405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040518082815260200191505060405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561098c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526025815260200180610d986025913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610a12576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610d056023913960400191505060405180910390fd5b610a7d81604051806060016040528060268152602001610d4a602691396000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610bbc9092919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610b10816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610c7c90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b6000838311158290610c69576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610c2e578082015181840152602081019050610c13565b50505050905090810190601f168015610c5b5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b600080828401905083811015610cfa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b809150509291505056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa265627a7a72315820c7a5ffabf642bda14700b2de42f8c57b36621af020441df825de45fd2b3e1c5c64736f6c63430005100032", 261 | "sourceMap": "166:93:0:-;;;196:61;8:9:-1;5:2;;;30:1;27;20:12;5:2;196:61:0;223:29;229:10;241;223:5;;;:29;;:::i;:::-;166:93;;5962:302:3;6056:1;6037:21;;:7;:21;;;;6029:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6120:24;6137:6;6120:12;;:16;;;;;;:24;;;;:::i;:::-;6105:12;:39;;;;6175:30;6198:6;6175:9;:18;6185:7;6175:18;;;;;;;;;;;;;;;;:22;;;;;;:30;;;;:::i;:::-;6154:9;:18;6164:7;6154:18;;;;;;;;;;;;;;;:51;;;;6241:7;6220:37;;6237:1;6220:37;;;6250:6;6220:37;;;;;;;;;;;;;;;;;;5962:302;;:::o;834:176:2:-;892:7;911:9;927:1;923;:5;911:17;;951:1;946;:6;;938:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1002:1;995:8;;;834:176;;;;:::o;166:93:0:-;;;;;;;", 262 | "deployedSourceMap": "166:93:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;166:93:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2500:149:3;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2500:149:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1559:89;;;:::i;:::-;;;;;;;;;;;;;;;;;;;3107:300;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3107:300:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;3802:207;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;3802:207:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1706:108;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;1706:108:3;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;4496:258;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4496:258:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;2017:155;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2017:155:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;2230:132;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2230:132:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;2500:149;2566:4;2582:39;2591:12;:10;:12::i;:::-;2605:7;2614:6;2582:8;:39::i;:::-;2638:4;2631:11;;2500:149;;;;:::o;1559:89::-;1603:7;1629:12;;1622:19;;1559:89;:::o;3107:300::-;3196:4;3212:36;3222:6;3230:9;3241:6;3212:9;:36::i;:::-;3258:121;3267:6;3275:12;:10;:12::i;:::-;3289:89;3327:6;3289:89;;;;;;;;;;;;;;;;;:11;:19;3301:6;3289:19;;;;;;;;;;;;;;;:33;3309:12;:10;:12::i;:::-;3289:33;;;;;;;;;;;;;;;;:37;;:89;;;;;:::i;:::-;3258:8;:121::i;:::-;3396:4;3389:11;;3107:300;;;;;:::o;3802:207::-;3882:4;3898:83;3907:12;:10;:12::i;:::-;3921:7;3930:50;3969:10;3930:11;:25;3942:12;:10;:12::i;:::-;3930:25;;;;;;;;;;;;;;;:34;3956:7;3930:34;;;;;;;;;;;;;;;;:38;;:50;;;;:::i;:::-;3898:8;:83::i;:::-;3998:4;3991:11;;3802:207;;;;:::o;1706:108::-;1763:7;1789:9;:18;1799:7;1789:18;;;;;;;;;;;;;;;;1782:25;;1706:108;;;:::o;4496:258::-;4581:4;4597:129;4606:12;:10;:12::i;:::-;4620:7;4629:96;4668:15;4629:96;;;;;;;;;;;;;;;;;:11;:25;4641:12;:10;:12::i;:::-;4629:25;;;;;;;;;;;;;;;:34;4655:7;4629:34;;;;;;;;;;;;;;;;:38;;:96;;;;;:::i;:::-;4597:8;:129::i;:::-;4743:4;4736:11;;4496:258;;;;:::o;2017:155::-;2086:4;2102:42;2112:12;:10;:12::i;:::-;2126:9;2137:6;2102:9;:42::i;:::-;2161:4;2154:11;;2017:155;;;;:::o;2230:132::-;2302:7;2328:11;:18;2340:5;2328:18;;;;;;;;;;;;;;;:27;2347:7;2328:27;;;;;;;;;;;;;;;;2321:34;;2230:132;;;;:::o;788:96:1:-;833:15;867:10;860:17;;788:96;:::o;7350:332:3:-;7460:1;7443:19;;:5;:19;;;;7435:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7540:1;7521:21;;:7;:21;;;;7513:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7622:6;7592:11;:18;7604:5;7592:18;;;;;;;;;;;;;;;:27;7611:7;7592:27;;;;;;;;;;;;;;;:36;;;;7659:7;7643:32;;7652:5;7643:32;;;7668:6;7643:32;;;;;;;;;;;;;;;;;;7350:332;;;:::o;5228:464::-;5343:1;5325:20;;:6;:20;;;;5317:70;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5426:1;5405:23;;:9;:23;;;;5397:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5499;5521:6;5499:71;;;;;;;;;;;;;;;;;:9;:17;5509:6;5499:17;;;;;;;;;;;;;;;;:21;;:71;;;;;:::i;:::-;5479:9;:17;5489:6;5479:17;;;;;;;;;;;;;;;:91;;;;5603:32;5628:6;5603:9;:20;5613:9;5603:20;;;;;;;;;;;;;;;;:24;;:32;;;;:::i;:::-;5580:9;:20;5590:9;5580:20;;;;;;;;;;;;;;;:55;;;;5667:9;5650:35;;5659:6;5650:35;;;5678:6;5650:35;;;;;;;;;;;;;;;;;;5228:464;;;:::o;1732:187:2:-;1818:7;1850:1;1845;:6;;1853:12;1837:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;1837:29:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1876:9;1892:1;1888;:5;1876:17;;1911:1;1904:8;;;1732:187;;;;;:::o;834:176::-;892:7;911:9;927:1;923;:5;911:17;;951:1;946;:6;;938:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1002:1;995:8;;;834:176;;;;:::o", 263 | "source": "pragma solidity ^0.5.0;\n\nimport '@openzeppelin/contracts/token/ERC20/ERC20.sol';\n\n// This ERC-20 contract mints the maximum amount of tokens to the contract creator.\ncontract MyToken is ERC20 {\n constructor() public {\n _mint(msg.sender, 2**256 - 1);\n }\n}\n", 264 | "sourcePath": "/home/dan/Code/Substrate/substrate-evm/contracts/contracts/MyToken.sol", 265 | "ast": { 266 | "absolutePath": "/home/dan/Code/Substrate/substrate-evm/contracts/contracts/MyToken.sol", 267 | "exportedSymbols": { 268 | "MyToken": [ 269 | 19 270 | ] 271 | }, 272 | "id": 20, 273 | "nodeType": "SourceUnit", 274 | "nodes": [ 275 | { 276 | "id": 1, 277 | "literals": [ 278 | "solidity", 279 | "^", 280 | "0.5", 281 | ".0" 282 | ], 283 | "nodeType": "PragmaDirective", 284 | "src": "0:23:0" 285 | }, 286 | { 287 | "absolutePath": "@openzeppelin/contracts/token/ERC20/ERC20.sol", 288 | "file": "@openzeppelin/contracts/token/ERC20/ERC20.sol", 289 | "id": 2, 290 | "nodeType": "ImportDirective", 291 | "scope": 20, 292 | "sourceUnit": 639, 293 | "src": "25:55:0", 294 | "symbolAliases": [], 295 | "unitAlias": "" 296 | }, 297 | { 298 | "baseContracts": [ 299 | { 300 | "arguments": null, 301 | "baseName": { 302 | "contractScope": null, 303 | "id": 3, 304 | "name": "ERC20", 305 | "nodeType": "UserDefinedTypeName", 306 | "referencedDeclaration": 638, 307 | "src": "186:5:0", 308 | "typeDescriptions": { 309 | "typeIdentifier": "t_contract$_ERC20_$638", 310 | "typeString": "contract ERC20" 311 | } 312 | }, 313 | "id": 4, 314 | "nodeType": "InheritanceSpecifier", 315 | "src": "186:5:0" 316 | } 317 | ], 318 | "contractDependencies": [ 319 | 46, 320 | 638, 321 | 707 322 | ], 323 | "contractKind": "contract", 324 | "documentation": null, 325 | "fullyImplemented": true, 326 | "id": 19, 327 | "linearizedBaseContracts": [ 328 | 19, 329 | 638, 330 | 707, 331 | 46 332 | ], 333 | "name": "MyToken", 334 | "nodeType": "ContractDefinition", 335 | "nodes": [ 336 | { 337 | "body": { 338 | "id": 17, 339 | "nodeType": "Block", 340 | "src": "217:40:0", 341 | "statements": [ 342 | { 343 | "expression": { 344 | "argumentTypes": null, 345 | "arguments": [ 346 | { 347 | "argumentTypes": null, 348 | "expression": { 349 | "argumentTypes": null, 350 | "id": 8, 351 | "name": "msg", 352 | "nodeType": "Identifier", 353 | "overloadedDeclarations": [], 354 | "referencedDeclaration": 722, 355 | "src": "229:3:0", 356 | "typeDescriptions": { 357 | "typeIdentifier": "t_magic_message", 358 | "typeString": "msg" 359 | } 360 | }, 361 | "id": 9, 362 | "isConstant": false, 363 | "isLValue": false, 364 | "isPure": false, 365 | "lValueRequested": false, 366 | "memberName": "sender", 367 | "nodeType": "MemberAccess", 368 | "referencedDeclaration": null, 369 | "src": "229:10:0", 370 | "typeDescriptions": { 371 | "typeIdentifier": "t_address_payable", 372 | "typeString": "address payable" 373 | } 374 | }, 375 | { 376 | "argumentTypes": null, 377 | "commonType": { 378 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1", 379 | "typeString": "int_const 1157...(70 digits omitted)...9935" 380 | }, 381 | "id": 14, 382 | "isConstant": false, 383 | "isLValue": false, 384 | "isPure": true, 385 | "lValueRequested": false, 386 | "leftExpression": { 387 | "argumentTypes": null, 388 | "commonType": { 389 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639936_by_1", 390 | "typeString": "int_const 1157...(70 digits omitted)...9936" 391 | }, 392 | "id": 12, 393 | "isConstant": false, 394 | "isLValue": false, 395 | "isPure": true, 396 | "lValueRequested": false, 397 | "leftExpression": { 398 | "argumentTypes": null, 399 | "hexValue": "32", 400 | "id": 10, 401 | "isConstant": false, 402 | "isLValue": false, 403 | "isPure": true, 404 | "kind": "number", 405 | "lValueRequested": false, 406 | "nodeType": "Literal", 407 | "src": "241:1:0", 408 | "subdenomination": null, 409 | "typeDescriptions": { 410 | "typeIdentifier": "t_rational_2_by_1", 411 | "typeString": "int_const 2" 412 | }, 413 | "value": "2" 414 | }, 415 | "nodeType": "BinaryOperation", 416 | "operator": "**", 417 | "rightExpression": { 418 | "argumentTypes": null, 419 | "hexValue": "323536", 420 | "id": 11, 421 | "isConstant": false, 422 | "isLValue": false, 423 | "isPure": true, 424 | "kind": "number", 425 | "lValueRequested": false, 426 | "nodeType": "Literal", 427 | "src": "244:3:0", 428 | "subdenomination": null, 429 | "typeDescriptions": { 430 | "typeIdentifier": "t_rational_256_by_1", 431 | "typeString": "int_const 256" 432 | }, 433 | "value": "256" 434 | }, 435 | "src": "241:6:0", 436 | "typeDescriptions": { 437 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639936_by_1", 438 | "typeString": "int_const 1157...(70 digits omitted)...9936" 439 | } 440 | }, 441 | "nodeType": "BinaryOperation", 442 | "operator": "-", 443 | "rightExpression": { 444 | "argumentTypes": null, 445 | "hexValue": "31", 446 | "id": 13, 447 | "isConstant": false, 448 | "isLValue": false, 449 | "isPure": true, 450 | "kind": "number", 451 | "lValueRequested": false, 452 | "nodeType": "Literal", 453 | "src": "250:1:0", 454 | "subdenomination": null, 455 | "typeDescriptions": { 456 | "typeIdentifier": "t_rational_1_by_1", 457 | "typeString": "int_const 1" 458 | }, 459 | "value": "1" 460 | }, 461 | "src": "241:10:0", 462 | "typeDescriptions": { 463 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1", 464 | "typeString": "int_const 1157...(70 digits omitted)...9935" 465 | } 466 | } 467 | ], 468 | "expression": { 469 | "argumentTypes": [ 470 | { 471 | "typeIdentifier": "t_address_payable", 472 | "typeString": "address payable" 473 | }, 474 | { 475 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1", 476 | "typeString": "int_const 1157...(70 digits omitted)...9935" 477 | } 478 | ], 479 | "id": 7, 480 | "name": "_mint", 481 | "nodeType": "Identifier", 482 | "overloadedDeclarations": [], 483 | "referencedDeclaration": 522, 484 | "src": "223:5:0", 485 | "typeDescriptions": { 486 | "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", 487 | "typeString": "function (address,uint256)" 488 | } 489 | }, 490 | "id": 15, 491 | "isConstant": false, 492 | "isLValue": false, 493 | "isPure": false, 494 | "kind": "functionCall", 495 | "lValueRequested": false, 496 | "names": [], 497 | "nodeType": "FunctionCall", 498 | "src": "223:29:0", 499 | "typeDescriptions": { 500 | "typeIdentifier": "t_tuple$__$", 501 | "typeString": "tuple()" 502 | } 503 | }, 504 | "id": 16, 505 | "nodeType": "ExpressionStatement", 506 | "src": "223:29:0" 507 | } 508 | ] 509 | }, 510 | "documentation": null, 511 | "id": 18, 512 | "implemented": true, 513 | "kind": "constructor", 514 | "modifiers": [], 515 | "name": "", 516 | "nodeType": "FunctionDefinition", 517 | "parameters": { 518 | "id": 5, 519 | "nodeType": "ParameterList", 520 | "parameters": [], 521 | "src": "207:2:0" 522 | }, 523 | "returnParameters": { 524 | "id": 6, 525 | "nodeType": "ParameterList", 526 | "parameters": [], 527 | "src": "217:0:0" 528 | }, 529 | "scope": 19, 530 | "src": "196:61:0", 531 | "stateMutability": "nonpayable", 532 | "superFunction": null, 533 | "visibility": "public" 534 | } 535 | ], 536 | "scope": 20, 537 | "src": "166:93:0" 538 | } 539 | ], 540 | "src": "0:260:0" 541 | }, 542 | "legacyAST": { 543 | "absolutePath": "/home/dan/Code/Substrate/substrate-evm/contracts/contracts/MyToken.sol", 544 | "exportedSymbols": { 545 | "MyToken": [ 546 | 19 547 | ] 548 | }, 549 | "id": 20, 550 | "nodeType": "SourceUnit", 551 | "nodes": [ 552 | { 553 | "id": 1, 554 | "literals": [ 555 | "solidity", 556 | "^", 557 | "0.5", 558 | ".0" 559 | ], 560 | "nodeType": "PragmaDirective", 561 | "src": "0:23:0" 562 | }, 563 | { 564 | "absolutePath": "@openzeppelin/contracts/token/ERC20/ERC20.sol", 565 | "file": "@openzeppelin/contracts/token/ERC20/ERC20.sol", 566 | "id": 2, 567 | "nodeType": "ImportDirective", 568 | "scope": 20, 569 | "sourceUnit": 639, 570 | "src": "25:55:0", 571 | "symbolAliases": [], 572 | "unitAlias": "" 573 | }, 574 | { 575 | "baseContracts": [ 576 | { 577 | "arguments": null, 578 | "baseName": { 579 | "contractScope": null, 580 | "id": 3, 581 | "name": "ERC20", 582 | "nodeType": "UserDefinedTypeName", 583 | "referencedDeclaration": 638, 584 | "src": "186:5:0", 585 | "typeDescriptions": { 586 | "typeIdentifier": "t_contract$_ERC20_$638", 587 | "typeString": "contract ERC20" 588 | } 589 | }, 590 | "id": 4, 591 | "nodeType": "InheritanceSpecifier", 592 | "src": "186:5:0" 593 | } 594 | ], 595 | "contractDependencies": [ 596 | 46, 597 | 638, 598 | 707 599 | ], 600 | "contractKind": "contract", 601 | "documentation": null, 602 | "fullyImplemented": true, 603 | "id": 19, 604 | "linearizedBaseContracts": [ 605 | 19, 606 | 638, 607 | 707, 608 | 46 609 | ], 610 | "name": "MyToken", 611 | "nodeType": "ContractDefinition", 612 | "nodes": [ 613 | { 614 | "body": { 615 | "id": 17, 616 | "nodeType": "Block", 617 | "src": "217:40:0", 618 | "statements": [ 619 | { 620 | "expression": { 621 | "argumentTypes": null, 622 | "arguments": [ 623 | { 624 | "argumentTypes": null, 625 | "expression": { 626 | "argumentTypes": null, 627 | "id": 8, 628 | "name": "msg", 629 | "nodeType": "Identifier", 630 | "overloadedDeclarations": [], 631 | "referencedDeclaration": 722, 632 | "src": "229:3:0", 633 | "typeDescriptions": { 634 | "typeIdentifier": "t_magic_message", 635 | "typeString": "msg" 636 | } 637 | }, 638 | "id": 9, 639 | "isConstant": false, 640 | "isLValue": false, 641 | "isPure": false, 642 | "lValueRequested": false, 643 | "memberName": "sender", 644 | "nodeType": "MemberAccess", 645 | "referencedDeclaration": null, 646 | "src": "229:10:0", 647 | "typeDescriptions": { 648 | "typeIdentifier": "t_address_payable", 649 | "typeString": "address payable" 650 | } 651 | }, 652 | { 653 | "argumentTypes": null, 654 | "commonType": { 655 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1", 656 | "typeString": "int_const 1157...(70 digits omitted)...9935" 657 | }, 658 | "id": 14, 659 | "isConstant": false, 660 | "isLValue": false, 661 | "isPure": true, 662 | "lValueRequested": false, 663 | "leftExpression": { 664 | "argumentTypes": null, 665 | "commonType": { 666 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639936_by_1", 667 | "typeString": "int_const 1157...(70 digits omitted)...9936" 668 | }, 669 | "id": 12, 670 | "isConstant": false, 671 | "isLValue": false, 672 | "isPure": true, 673 | "lValueRequested": false, 674 | "leftExpression": { 675 | "argumentTypes": null, 676 | "hexValue": "32", 677 | "id": 10, 678 | "isConstant": false, 679 | "isLValue": false, 680 | "isPure": true, 681 | "kind": "number", 682 | "lValueRequested": false, 683 | "nodeType": "Literal", 684 | "src": "241:1:0", 685 | "subdenomination": null, 686 | "typeDescriptions": { 687 | "typeIdentifier": "t_rational_2_by_1", 688 | "typeString": "int_const 2" 689 | }, 690 | "value": "2" 691 | }, 692 | "nodeType": "BinaryOperation", 693 | "operator": "**", 694 | "rightExpression": { 695 | "argumentTypes": null, 696 | "hexValue": "323536", 697 | "id": 11, 698 | "isConstant": false, 699 | "isLValue": false, 700 | "isPure": true, 701 | "kind": "number", 702 | "lValueRequested": false, 703 | "nodeType": "Literal", 704 | "src": "244:3:0", 705 | "subdenomination": null, 706 | "typeDescriptions": { 707 | "typeIdentifier": "t_rational_256_by_1", 708 | "typeString": "int_const 256" 709 | }, 710 | "value": "256" 711 | }, 712 | "src": "241:6:0", 713 | "typeDescriptions": { 714 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639936_by_1", 715 | "typeString": "int_const 1157...(70 digits omitted)...9936" 716 | } 717 | }, 718 | "nodeType": "BinaryOperation", 719 | "operator": "-", 720 | "rightExpression": { 721 | "argumentTypes": null, 722 | "hexValue": "31", 723 | "id": 13, 724 | "isConstant": false, 725 | "isLValue": false, 726 | "isPure": true, 727 | "kind": "number", 728 | "lValueRequested": false, 729 | "nodeType": "Literal", 730 | "src": "250:1:0", 731 | "subdenomination": null, 732 | "typeDescriptions": { 733 | "typeIdentifier": "t_rational_1_by_1", 734 | "typeString": "int_const 1" 735 | }, 736 | "value": "1" 737 | }, 738 | "src": "241:10:0", 739 | "typeDescriptions": { 740 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1", 741 | "typeString": "int_const 1157...(70 digits omitted)...9935" 742 | } 743 | } 744 | ], 745 | "expression": { 746 | "argumentTypes": [ 747 | { 748 | "typeIdentifier": "t_address_payable", 749 | "typeString": "address payable" 750 | }, 751 | { 752 | "typeIdentifier": "t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1", 753 | "typeString": "int_const 1157...(70 digits omitted)...9935" 754 | } 755 | ], 756 | "id": 7, 757 | "name": "_mint", 758 | "nodeType": "Identifier", 759 | "overloadedDeclarations": [], 760 | "referencedDeclaration": 522, 761 | "src": "223:5:0", 762 | "typeDescriptions": { 763 | "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", 764 | "typeString": "function (address,uint256)" 765 | } 766 | }, 767 | "id": 15, 768 | "isConstant": false, 769 | "isLValue": false, 770 | "isPure": false, 771 | "kind": "functionCall", 772 | "lValueRequested": false, 773 | "names": [], 774 | "nodeType": "FunctionCall", 775 | "src": "223:29:0", 776 | "typeDescriptions": { 777 | "typeIdentifier": "t_tuple$__$", 778 | "typeString": "tuple()" 779 | } 780 | }, 781 | "id": 16, 782 | "nodeType": "ExpressionStatement", 783 | "src": "223:29:0" 784 | } 785 | ] 786 | }, 787 | "documentation": null, 788 | "id": 18, 789 | "implemented": true, 790 | "kind": "constructor", 791 | "modifiers": [], 792 | "name": "", 793 | "nodeType": "FunctionDefinition", 794 | "parameters": { 795 | "id": 5, 796 | "nodeType": "ParameterList", 797 | "parameters": [], 798 | "src": "207:2:0" 799 | }, 800 | "returnParameters": { 801 | "id": 6, 802 | "nodeType": "ParameterList", 803 | "parameters": [], 804 | "src": "217:0:0" 805 | }, 806 | "scope": 19, 807 | "src": "196:61:0", 808 | "stateMutability": "nonpayable", 809 | "superFunction": null, 810 | "visibility": "public" 811 | } 812 | ], 813 | "scope": 20, 814 | "src": "166:93:0" 815 | } 816 | ], 817 | "src": "0:260:0" 818 | }, 819 | "compiler": { 820 | "name": "solc", 821 | "version": "0.5.16+commit.9c3226ce.Emscripten.clang" 822 | }, 823 | "networks": {}, 824 | "schemaVersion": "3.0.23", 825 | "updatedAt": "2020-02-14T16:09:00.260Z", 826 | "devdoc": { 827 | "methods": { 828 | "allowance(address,address)": { 829 | "details": "See {IERC20-allowance}." 830 | }, 831 | "approve(address,uint256)": { 832 | "details": "See {IERC20-approve}. * Requirements: * - `spender` cannot be the zero address." 833 | }, 834 | "balanceOf(address)": { 835 | "details": "See {IERC20-balanceOf}." 836 | }, 837 | "decreaseAllowance(address,uint256)": { 838 | "details": "Atomically decreases the allowance granted to `spender` by the caller. * This is an alternative to {approve} that can be used as a mitigation for problems described in {IERC20-approve}. * Emits an {Approval} event indicating the updated allowance. * Requirements: * - `spender` cannot be the zero address. - `spender` must have allowance for the caller of at least `subtractedValue`." 839 | }, 840 | "increaseAllowance(address,uint256)": { 841 | "details": "Atomically increases the allowance granted to `spender` by the caller. * This is an alternative to {approve} that can be used as a mitigation for problems described in {IERC20-approve}. * Emits an {Approval} event indicating the updated allowance. * Requirements: * - `spender` cannot be the zero address." 842 | }, 843 | "totalSupply()": { 844 | "details": "See {IERC20-totalSupply}." 845 | }, 846 | "transfer(address,uint256)": { 847 | "details": "See {IERC20-transfer}. * Requirements: * - `recipient` cannot be the zero address. - the caller must have a balance of at least `amount`." 848 | }, 849 | "transferFrom(address,address,uint256)": { 850 | "details": "See {IERC20-transferFrom}. * Emits an {Approval} event indicating the updated allowance. This is not required by the EIP. See the note at the beginning of {ERC20}; * Requirements: - `sender` and `recipient` cannot be the zero address. - `sender` must have a balance of at least `amount`. - the caller must have allowance for `sender`'s tokens of at least `amount`." 851 | } 852 | } 853 | }, 854 | "userdoc": { 855 | "methods": {} 856 | } 857 | } -------------------------------------------------------------------------------- /truffle/contracts/MyToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | 5 | // This ERC-20 contract mints the maximum amount of tokens to the contract creator. 6 | contract MyToken is ERC20 { 7 | constructor() public { 8 | _mint(msg.sender, 2**256 - 1); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /truffle/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "substrate-evm-contracts", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@openzeppelin/contracts": { 8 | "version": "2.5.0", 9 | "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-2.5.0.tgz", 10 | "integrity": "sha512-t3jm8FrhL9tkkJTofkznTqo/XXdHi21w5yXwalEnaMOp22ZwZ0f/mmKdlgMMLPFa6bSVHbY88mKESwJT/7m5Lg==" 11 | }, 12 | "app-module-path": { 13 | "version": "2.2.0", 14 | "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", 15 | "integrity": "sha1-ZBqlXft9am8KgUHEucCqULbCTdU=" 16 | }, 17 | "balanced-match": { 18 | "version": "1.0.0", 19 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 20 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 21 | }, 22 | "brace-expansion": { 23 | "version": "1.1.11", 24 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 25 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 26 | "requires": { 27 | "balanced-match": "^1.0.0", 28 | "concat-map": "0.0.1" 29 | } 30 | }, 31 | "browser-stdout": { 32 | "version": "1.3.1", 33 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 34 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" 35 | }, 36 | "commander": { 37 | "version": "2.15.1", 38 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 39 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" 40 | }, 41 | "concat-map": { 42 | "version": "0.0.1", 43 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 44 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 45 | }, 46 | "debug": { 47 | "version": "3.1.0", 48 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 49 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 50 | "requires": { 51 | "ms": "2.0.0" 52 | } 53 | }, 54 | "diff": { 55 | "version": "3.5.0", 56 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 57 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" 58 | }, 59 | "escape-string-regexp": { 60 | "version": "1.0.5", 61 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 62 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 63 | }, 64 | "fs.realpath": { 65 | "version": "1.0.0", 66 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 67 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 68 | }, 69 | "glob": { 70 | "version": "7.1.2", 71 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 72 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 73 | "requires": { 74 | "fs.realpath": "^1.0.0", 75 | "inflight": "^1.0.4", 76 | "inherits": "2", 77 | "minimatch": "^3.0.4", 78 | "once": "^1.3.0", 79 | "path-is-absolute": "^1.0.0" 80 | } 81 | }, 82 | "growl": { 83 | "version": "1.10.5", 84 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 85 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" 86 | }, 87 | "has-flag": { 88 | "version": "3.0.0", 89 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 90 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 91 | }, 92 | "he": { 93 | "version": "1.1.1", 94 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 95 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" 96 | }, 97 | "inflight": { 98 | "version": "1.0.6", 99 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 100 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 101 | "requires": { 102 | "once": "^1.3.0", 103 | "wrappy": "1" 104 | } 105 | }, 106 | "inherits": { 107 | "version": "2.0.4", 108 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 109 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 110 | }, 111 | "minimatch": { 112 | "version": "3.0.4", 113 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 114 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 115 | "requires": { 116 | "brace-expansion": "^1.1.7" 117 | } 118 | }, 119 | "minimist": { 120 | "version": "0.0.8", 121 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 122 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 123 | }, 124 | "mkdirp": { 125 | "version": "0.5.1", 126 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 127 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 128 | "requires": { 129 | "minimist": "0.0.8" 130 | } 131 | }, 132 | "mocha": { 133 | "version": "5.2.0", 134 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 135 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 136 | "requires": { 137 | "browser-stdout": "1.3.1", 138 | "commander": "2.15.1", 139 | "debug": "3.1.0", 140 | "diff": "3.5.0", 141 | "escape-string-regexp": "1.0.5", 142 | "glob": "7.1.2", 143 | "growl": "1.10.5", 144 | "he": "1.1.1", 145 | "minimatch": "3.0.4", 146 | "mkdirp": "0.5.1", 147 | "supports-color": "5.4.0" 148 | } 149 | }, 150 | "ms": { 151 | "version": "2.0.0", 152 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 153 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 154 | }, 155 | "once": { 156 | "version": "1.4.0", 157 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 158 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 159 | "requires": { 160 | "wrappy": "1" 161 | } 162 | }, 163 | "original-require": { 164 | "version": "1.0.1", 165 | "resolved": "https://registry.npmjs.org/original-require/-/original-require-1.0.1.tgz", 166 | "integrity": "sha1-DxMEcVhM0zURxew4yNWSE/msXiA=" 167 | }, 168 | "path-is-absolute": { 169 | "version": "1.0.1", 170 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 171 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 172 | }, 173 | "supports-color": { 174 | "version": "5.4.0", 175 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 176 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 177 | "requires": { 178 | "has-flag": "^3.0.0" 179 | } 180 | }, 181 | "truffle": { 182 | "version": "5.1.12", 183 | "resolved": "https://registry.npmjs.org/truffle/-/truffle-5.1.12.tgz", 184 | "integrity": "sha512-lw5NC3S4esGqKIbqKXWutOcOMz3G+2LCKvacnrwH/PNh/Wk0qPKia7daiLJqRdqjJkHPtAUpRf8b1aqrcot2Ww==", 185 | "requires": { 186 | "app-module-path": "^2.2.0", 187 | "mocha": "5.2.0", 188 | "original-require": "1.0.1" 189 | } 190 | }, 191 | "wrappy": { 192 | "version": "1.0.2", 193 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 194 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /truffle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "substrate-evm-contracts", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@openzeppelin/contracts": "^2.5.0", 6 | "truffle": "^5.1.12" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /truffle/truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | // const HDWalletProvider = require('truffle-hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | // const fs = require('fs'); 25 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | 51 | // Another network with more advanced options... 52 | // advanced: { 53 | // port: 8777, // Custom port 54 | // network_id: 1342, // Custom network 55 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 56 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 57 | // from:
, // Account to send txs from (default: accounts[0]) 58 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 59 | // }, 60 | 61 | // Useful for deploying to a public network. 62 | // NB: It's important to wrap the provider as a function. 63 | // ropsten: { 64 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 65 | // network_id: 3, // Ropsten's id 66 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 67 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 68 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 69 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 70 | // }, 71 | 72 | // Useful for private networks 73 | // private: { 74 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 75 | // network_id: 2111, // This network is yours, in the cloud. 76 | // production: true // Treats this network as if it was a public net. (default: false) 77 | // } 78 | }, 79 | 80 | // Set default mocha options here, use special reporters etc. 81 | mocha: { 82 | // timeout: 100000 83 | }, 84 | 85 | // Configure your compilers 86 | compilers: { 87 | solc: { 88 | // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) 89 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 90 | // settings: { // See the solidity docs for advice about optimization and evmVersion 91 | // optimizer: { 92 | // enabled: false, 93 | // runs: 200 94 | // }, 95 | // evmVersion: "byzantium" 96 | // } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ui/.env: -------------------------------------------------------------------------------- 1 | EXTEND_ESLINT=true 2 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | **/.DS_Store 4 | build 5 | -------------------------------------------------------------------------------- /ui/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 | -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | # Substrate Node + EVM Pallet Front-End 2 | 3 | This front-end project makes it easier for the user to interact with [Substrate's](https://substrate.dev/en/) [EVM pallet](https://substrate.dev/docs/en/next/conceptual/runtime/frame#evm). 4 | 5 | ## Upstream 6 | 7 | This project was forked from the [Substrate Front-End Template](https://github.com/substrate-developer-hub/substrate-front-end-template) and the same instructions for [installation](https://github.com/substrate-developer-hub/substrate-front-end-template#installation), [usage](https://github.com/substrate-developer-hub/substrate-front-end-template#usage) and [configuration](https://github.com/substrate-developer-hub/substrate-front-end-template#configuration) apply. 8 | 9 | ## Components 10 | 11 | This UI includes two custom components: [EVM Accounts](/ui/src/EVMAccounts.js) and [EVM Contracts](/ui/src/EVMContracts.js). 12 | -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "substrate-front-end-template", 3 | "version": "0.1.1", 4 | "private": true, 5 | "author": "Parity Technologies ", 6 | "license": "Unlicense", 7 | "dependencies": { 8 | "@polkadot/api": "^1.0.0-beta.7", 9 | "@polkadot/extension-dapp": "^0.14.1", 10 | "@polkadot/keyring": "^2.0.0-beta.4", 11 | "@polkadot/react-identicon": "^0.47.1", 12 | "@polkadot/ui-keyring": "^0.47.1", 13 | "@polkadot/util": "^2.0.0-beta.4", 14 | "@polkadot/util-crypto": "^2.0.0-beta.4", 15 | "big.js": "^5.2.2", 16 | "keccak": "^3.0.0", 17 | "prop-types": "^15.7.2", 18 | "react": "^16.12.0", 19 | "react-dom": "^16.12.0", 20 | "react-scripts": "3.2.0", 21 | "rlp": "^2.2.4", 22 | "semantic-ui-css": "^2.4.1", 23 | "semantic-ui-react": "^0.88.0", 24 | "toformat": "^2.0.0" 25 | }, 26 | "devDependencies": { 27 | "eslint-config-standard": "^14.1.0", 28 | "eslint-plugin-import": "^2.18.2", 29 | "eslint-plugin-node": "^10.0.0", 30 | "eslint-plugin-only-warn": "^1.0.1", 31 | "eslint-plugin-promise": "^4.2.1", 32 | "eslint-plugin-standard": "^4.0.1", 33 | "gh-pages": "^2.1.1" 34 | }, 35 | "scripts": { 36 | "start": "PORT=8221 react-scripts start", 37 | "build": "react-scripts build", 38 | "test": "CI=true react-scripts test --env=jsdom", 39 | "eject": "react-scripts eject", 40 | "lint": "eslint src/**", 41 | "lint:ci": "eslint src/** --max-warnings=0", 42 | "lint:fix": "eslint --fix src/**", 43 | "predeploy": "yarn build", 44 | "deploy": "gh-pages -d build -m '[ci skip] Updates'" 45 | }, 46 | "eslintConfig": { 47 | "extends": [ 48 | "react-app", 49 | "standard" 50 | ], 51 | "plugins": [ 52 | "only-warn" 53 | ], 54 | "rules": { 55 | "semi": [ 56 | 1, 57 | "always" 58 | ], 59 | "no-extra-semi": 1 60 | } 61 | }, 62 | "browserslist": { 63 | "production": [ 64 | ">0.2%", 65 | "not dead", 66 | "not op_mini all" 67 | ], 68 | "development": [ 69 | "last 1 chrome version", 70 | "last 1 firefox version", 71 | "last 1 safari version" 72 | ] 73 | }, 74 | "homepage": "https://substrate-developer-hub.github.io/substrate-front-end-template", 75 | "bugs": { 76 | "url": "https://github.com/substrate-developer-hub/substrate-front-end-template/issues" 77 | }, 78 | "keywords": [ 79 | "substrate", 80 | "substrate-ui", 81 | "polkadot-js" 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /ui/public/Substrate-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danforbes/substrate-evm/3994b9de1fa98007c4fe1d80a2872d11f29ab3f7/ui/public/Substrate-Logo.png -------------------------------------------------------------------------------- /ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danforbes/substrate-evm/3994b9de1fa98007c4fe1d80a2872d11f29ab3f7/ui/public/favicon.ico -------------------------------------------------------------------------------- /ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | Substrate + EVM Front End 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Substrate Front End", 3 | "name": "Substrate Front End Template", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 48x48 32x32 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ui/src/AccountSelector.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import { 4 | Menu, 5 | Dropdown, 6 | Container, 7 | Icon, 8 | Image, 9 | Label 10 | } from 'semantic-ui-react'; 11 | 12 | import { useSubstrate } from './substrate-lib'; 13 | 14 | export default function AccountSelector (props) { 15 | const { api, keyring } = useSubstrate(); 16 | const { setAccountAddress } = props; 17 | const [accountSelected, setAccountSelected] = useState(''); 18 | 19 | // Get the list of accounts we possess the private key for 20 | const keyringOptions = keyring.getPairs().map(account => ({ 21 | key: account.address, 22 | value: account.address, 23 | text: account.meta.name.toUpperCase(), 24 | icon: 'user' 25 | })); 26 | 27 | const initialAddress = 28 | keyringOptions.length > 0 ? keyringOptions[0].value : ''; 29 | 30 | // Set the initial address 31 | useEffect(() => { 32 | setAccountSelected(initialAddress); 33 | setAccountAddress(initialAddress); 34 | }, [setAccountAddress, initialAddress]); 35 | 36 | const onChange = address => { 37 | // Update state with new account address 38 | setAccountAddress(address); 39 | setAccountSelected(address); 40 | }; 41 | 42 | return ( 43 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | { onChange(dropdown.value); }} 71 | value={accountSelected} 72 | /> 73 | {api.query.balances && api.query.balances.freeBalance 74 | ? 75 | : null} 76 | 77 | 78 | 79 | ); 80 | } 81 | 82 | function BalanceAnnotation (props) { 83 | const { accountSelected } = props; 84 | const { api } = useSubstrate(); 85 | const [accountBalance, setAccountBalance] = useState(0); 86 | 87 | // When account address changes, update subscriptions 88 | useEffect(() => { 89 | let unsubscribe; 90 | 91 | // If the user has selected an address, create a new subscription 92 | accountSelected && 93 | api.query.balances.freeBalance(accountSelected, balance => { 94 | setAccountBalance(balance.toString()); 95 | }).then(unsub => { 96 | unsubscribe = unsub; 97 | }).catch(console.error); 98 | 99 | return () => unsubscribe && unsubscribe(); 100 | }, [accountSelected, api.query.balances]); 101 | 102 | return accountSelected 103 | ? 110 | : null; 111 | } 112 | -------------------------------------------------------------------------------- /ui/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, createRef } from 'react'; 2 | import { Container, Dimmer, Loader, Grid, Sticky } from 'semantic-ui-react'; 3 | 4 | import 'semantic-ui-css/semantic.min.css'; 5 | import { SubstrateContextProvider, useSubstrate } from './substrate-lib'; 6 | import { DeveloperConsole } from './substrate-lib/components'; 7 | 8 | import AccountSelector from './AccountSelector'; 9 | import BlockNumber from './BlockNumber'; 10 | import EVMAccounts from './EVMAccounts'; 11 | import EVMContracts from './EVMContracts'; 12 | import Metadata from './Metadata'; 13 | import NodeInfo from './NodeInfo'; 14 | 15 | function Main () { 16 | const [accountAddress, setAccountAddress] = useState(null); 17 | const { apiState, keyring, keyringState } = useSubstrate(); 18 | const accountPair = 19 | accountAddress && 20 | keyringState === 'READY' && 21 | keyring.getPair(accountAddress); 22 | 23 | const loader = text => ( 24 | 25 | {text} 26 | 27 | ); 28 | 29 | if (apiState === 'ERROR') return loader('Error connecting to the blockchain'); 30 | else if (apiState !== 'READY') return loader('Connecting to the blockchain'); 31 | 32 | if (keyringState !== 'READY') { 33 | return loader( 34 | "Loading accounts (please review any extension's authorization)" 35 | ); 36 | } 37 | 38 | const contextRef = createRef(); 39 | 40 | return ( 41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 | ); 64 | } 65 | 66 | export default function App () { 67 | return ( 68 | 69 |
70 | 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /ui/src/BlockNumber.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Statistic, Grid, Card, Icon } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | 6 | export default function BlockNumber (props) { 7 | const { api } = useSubstrate(); 8 | const { finalized } = props; 9 | const [blockNumber, setBlockNumber] = useState(0); 10 | const [blockNumberTimer, setBlockNumberTimer] = useState(0); 11 | 12 | const bestNumber = finalized 13 | ? api.derive.chain.bestNumberFinalized 14 | : api.derive.chain.bestNumber; 15 | 16 | useEffect(() => { 17 | let unsubscribeAll = null; 18 | 19 | bestNumber(number => { 20 | setBlockNumber(number.toNumber()); 21 | setBlockNumberTimer(0); 22 | }).then(unsub => { 23 | unsubscribeAll = unsub; 24 | }).catch(console.error); 25 | 26 | return () => unsubscribeAll && unsubscribeAll(); 27 | }, [bestNumber]); 28 | 29 | const timer = () => { 30 | setBlockNumberTimer(time => time + 1); 31 | }; 32 | 33 | useEffect(() => { 34 | const id = setInterval(timer, 1000); 35 | return () => clearInterval(id); 36 | }, []); 37 | 38 | return ( 39 | 40 | 41 | 42 | 46 | 47 | 48 | {blockNumberTimer} 49 | 50 | 51 | 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /ui/src/EVMAccounts.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Table, Grid } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate, utils } from './substrate-lib'; 5 | 6 | function Main (props) { 7 | const { api, keyring } = useSubstrate(); 8 | const accounts = keyring.getPairs(); 9 | const [balances, setBalances] = useState({}); 10 | 11 | useEffect(() => { 12 | const accountIDs = keyring.getPairs().map(account => utils.getEVMAccountID(keyring.decodeAddress(account.address))); 13 | let unsubscribeAll = null; 14 | api.query.evm.accounts.multi(accountIDs, (accounts) => { 15 | const balancesMap = accountIDs.reduce((acc, cur, idx) => { 16 | acc[cur] = utils.prettyBalance(accounts[idx].balance.toString(), { power: 18, decimal: 18, unit: 'ETH' }); 17 | return acc; 18 | }, {}); 19 | setBalances(balancesMap); 20 | }).then((unsubscribe) => { 21 | unsubscribeAll = unsubscribe; 22 | }).catch(console.error); 23 | 24 | return () => unsubscribeAll && unsubscribeAll(); 25 | }, [api.query.evm.accounts, keyring]); 26 | 27 | return ( 28 | 29 |

EVM Accounts

30 | 31 | {accounts.map(account => 32 | 33 | {account.meta.name} 34 | {utils.getEVMAccountID(keyring.decodeAddress(account.address))} 35 | {balances[utils.getEVMAccountID(keyring.decodeAddress(account.address))]} 36 | 37 | )} 38 | 39 |
40 |
41 | ); 42 | } 43 | 44 | export default function EVMAccounts (props) { 45 | const { api } = useSubstrate(); 46 | return (api.query.evm && api.query.evm.accounts ?
: null); 47 | } 48 | -------------------------------------------------------------------------------- /ui/src/EVMContracts.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Grid, Form, Input } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | import { TxButton } from './substrate-lib/components'; 6 | 7 | function Main (props) { 8 | const { accountPair, api, keyring } = props; 9 | const [status, setStatus] = useState({ code: null, message: null, events: null }); 10 | 11 | const [formState, setFormState] = useState({ 12 | bytecode: 0x0, 13 | initialBalance: 0, 14 | gasLimit: 0, 15 | gasPrice: 0 16 | }); 17 | const { bytecode, initialBalance, gasLimit, gasPrice } = formState; 18 | const onChange = (_, data) => setFormState(formState => ({ ...formState, [data.state]: data.value })); 19 | 20 | const [message, setMessage] = useState(null); 21 | useEffect(() => { 22 | const { code, message } = status; 23 | let unsubscribeAll = null; 24 | switch (code) { 25 | case 0: { 26 | if (!Array.isArray(status.events) || status.events.length < 1) { 27 | break; 28 | } 29 | 30 | const event = status.events[0].event; 31 | const eventName = event.meta.name.toString(); 32 | switch (eventName) { 33 | case 'Created': 34 | setMessage(`Contract ${event.data[0]} created at block ${message.substring(message.lastIndexOf(' ') + 1)}`); 35 | break; 36 | case 'ExtrinsicFailed': { 37 | const errorData = event.data[0]; 38 | if (!errorData.isModule) { 39 | break; 40 | } 41 | 42 | api.rpc.state.getMetadata((metadata) => { 43 | const { index, error } = errorData.asModule; 44 | const doc = metadata.metadata.asV11.modules[index].errors[error].documentation; 45 | setMessage(`Error creating transaction at block ${message.substring(message.lastIndexOf(' ') + 1)}: ${doc}`); 46 | }).then((unsubscribe) => { 47 | unsubscribeAll = unsubscribe; 48 | }).catch(console.error); 49 | 50 | return () => unsubscribeAll && unsubscribeAll(); 51 | } 52 | default: 53 | console.warn(`Unknown event: ${eventName}.`); 54 | } 55 | 56 | break; 57 | } 58 | default: 59 | setMessage(message); 60 | } 61 | }, [accountPair.address, api.query.evm, api.rpc.state, keyring, status]); 62 | 63 | return ( 64 | 65 |

EVM Contracts

66 |

Create Contract

67 |
68 | 69 | 77 | 78 | 79 | 87 | 88 | 89 | 97 | 98 | 99 | 107 | 108 | 109 | 119 | 120 |
{message}
121 |
122 |
123 | ); 124 | } 125 | 126 | export default function EVMContracts (_props) { 127 | const { api, keyring } = useSubstrate(); 128 | if (!api || !api.tx || !api.tx.evm || !api.tx.evm.create) { 129 | return null; 130 | } 131 | 132 | if (!_props.accountPair) { 133 | return null; 134 | } 135 | 136 | const props = { ..._props, api, keyring }; 137 | return
; 138 | } 139 | -------------------------------------------------------------------------------- /ui/src/Metadata.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Grid, Modal, Button, Card } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | 6 | export default function Metadata (props) { 7 | const { api } = useSubstrate(); 8 | const [metadata, setMetadata] = useState({ data: null, version: null }); 9 | 10 | useEffect(() => { 11 | const getMetadata = async () => { 12 | try { 13 | const data = await api.rpc.state.getMetadata(); 14 | setMetadata({ data, version: data.version }); 15 | } catch (e) { 16 | console.error(e); 17 | } 18 | }; 19 | getMetadata(); 20 | }, [api.rpc.state]); 21 | 22 | return ( 23 | 24 | 25 | 26 | Metadata 27 | v{metadata.version} 28 | 29 | 30 | Show Metadata}> 31 | Runtime Metadata 32 | 33 | 34 |
{JSON.stringify(metadata.data, null, 2)}
35 |
36 |
37 |
38 |
39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /ui/src/NodeInfo.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Card, Icon, Grid } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | 6 | export default function NodeInfo (props) { 7 | const { api } = useSubstrate(); 8 | const [nodeInfo, setNodeInfo] = useState({}); 9 | 10 | useEffect(() => { 11 | const getInfo = async () => { 12 | try { 13 | const [chain, nodeName, nodeVersion] = await Promise.all([ 14 | api.rpc.system.chain(), 15 | api.rpc.system.name(), 16 | api.rpc.system.version() 17 | ]); 18 | setNodeInfo({ chain, nodeName, nodeVersion }); 19 | } catch (e) { 20 | console.error(e); 21 | } 22 | }; 23 | getInfo(); 24 | }, [api.rpc.system]); 25 | 26 | return ( 27 | 28 | 29 | 30 | {nodeInfo.nodeName} 31 | 32 | {nodeInfo.chain} 33 | 34 | 35 | Built using the{' '} 36 | 37 | Substrate Front End Template 38 | 39 | 40 | 41 | 42 | v{nodeInfo.nodeVersion} 43 | 44 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /ui/src/__tests__/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from '../App'; 4 | 5 | describe('App Test Suite', () => { 6 | it('renders without crashing', () => { 7 | const div = document.createElement('div'); 8 | ReactDOM.render(, div); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /ui/src/config/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_NAME": "substrate-front-end-tutorial", 3 | "DEVELOPMENT_KEYRING": true, 4 | "CUSTOM_TYPES": { 5 | "Account": { 6 | "nonce": "U256", 7 | "balance": "U256" 8 | }, 9 | "AccountData": { 10 | "free": "U256", 11 | "reserved": "U256", 12 | "misc_frozen": "U256", 13 | "fee_frozen": "U256" 14 | }, 15 | "ChangesTrieConfiguration": { 16 | "digest_interval": "U32", 17 | "digest_levels": "U32" 18 | }, 19 | "Log": { 20 | "address": "H160", 21 | "topics": "Vec", 22 | "data": "Vec" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ui/src/config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "ws://127.0.0.1:9944" 3 | } 4 | -------------------------------------------------------------------------------- /ui/src/config/index.js: -------------------------------------------------------------------------------- 1 | import configCommon from './common.json'; 2 | // Using `require` as `import` does not support dynamic loading (yet). 3 | const configEnv = require(`./${process.env.NODE_ENV}.json`); 4 | 5 | // Accepting React env vars and aggregating them into `config` object. 6 | const envVarNames = [ 7 | 'REACT_APP_PROVIDER_SOCKET', 8 | 'REACT_APP_DEVELOPMENT_KEYRING' 9 | ]; 10 | const envVars = envVarNames.reduce((mem, n) => { 11 | if (process.env[n] !== undefined) mem[n.slice(10)] = process.env[n]; 12 | return mem; 13 | }, {}); 14 | 15 | const config = { ...configCommon, ...configEnv, ...envVars }; 16 | export default config; 17 | -------------------------------------------------------------------------------- /ui/src/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "wss://dev-node.substrate.dev:9944" 3 | } 4 | -------------------------------------------------------------------------------- /ui/src/config/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "wss://dev-node.substrate.dev:9944" 3 | } 4 | -------------------------------------------------------------------------------- /ui/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './App'; 5 | 6 | ReactDOM.render(, 7 | document.getElementById('root') 8 | ); 9 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/SubstrateContext.js: -------------------------------------------------------------------------------- 1 | import React, { useReducer } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import config from '../config'; 4 | 5 | const INIT_STATE = { 6 | socket: config.PROVIDER_SOCKET, 7 | types: config.CUSTOM_TYPES, 8 | keyring: null, 9 | keyringState: null, 10 | api: null, 11 | apiState: null 12 | }; 13 | 14 | const reducer = (state, action) => { 15 | let socket = null; 16 | 17 | switch (action.type) { 18 | case 'RESET_SOCKET': 19 | socket = action.payload || state.socket; 20 | return { ...state, socket, api: null, apiState: null }; 21 | 22 | case 'CONNECT': 23 | return { ...state, api: action.payload, apiState: 'CONNECTING' }; 24 | 25 | case 'CONNECT_SUCCESS': 26 | return { ...state, apiState: 'READY' }; 27 | 28 | case 'CONNECT_ERROR': 29 | return { ...state, apiState: 'ERROR' }; 30 | 31 | case 'SET_KEYRING': 32 | return { ...state, keyring: action.payload, keyringState: 'READY' }; 33 | 34 | case 'KEYRING_ERROR': 35 | return { ...state, keyring: null, keyringState: 'ERROR' }; 36 | 37 | default: 38 | throw new Error(`Unknown type: ${action.type}`); 39 | } 40 | }; 41 | 42 | const SubstrateContext = React.createContext(); 43 | 44 | const SubstrateContextProvider = (props) => { 45 | // filtering props and merge with default param value 46 | const initState = { ...INIT_STATE }; 47 | const neededPropNames = ['socket', 'types']; 48 | neededPropNames.forEach(key => { 49 | initState[key] = (typeof props[key] === 'undefined' ? initState[key] : props[key]); 50 | }); 51 | const [state, dispatch] = useReducer(reducer, initState); 52 | 53 | return ( 54 | 55 | {props.children} 56 | 57 | ); 58 | }; 59 | 60 | // prop typechecking 61 | SubstrateContextProvider.propTypes = { 62 | socket: PropTypes.string, 63 | types: PropTypes.object 64 | }; 65 | 66 | export { SubstrateContext, SubstrateContextProvider }; 67 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/components/DeveloperConsole.js: -------------------------------------------------------------------------------- 1 | // This component will simply add utility functions to your developer console. 2 | import { useSubstrate } from '../'; 3 | 4 | export default function DeveloperConsole (props) { 5 | const { api } = useSubstrate(); 6 | window.api = api; 7 | window.util = require('@polkadot/util'); 8 | window.util_crypto = require('@polkadot/util-crypto'); 9 | window.keyring = require('@polkadot/keyring'); 10 | 11 | return null; 12 | } 13 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/components/TxButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from 'semantic-ui-react'; 3 | import { web3FromSource } from '@polkadot/extension-dapp'; 4 | 5 | import { useSubstrate } from '../'; 6 | 7 | export default function TxButton ({ 8 | accountPair = null, 9 | label, 10 | setStatus, 11 | style = null, 12 | type = null, 13 | attrs = null, 14 | disabled = false 15 | }) { 16 | const { api } = useSubstrate(); 17 | const { params = null, sudo = false, tx = null } = attrs; 18 | const isQuery = () => type === 'QUERY'; 19 | 20 | const query = async () => { 21 | try { 22 | const result = await tx(...params); 23 | setStatus({ code: 0, message: result.toString() }); 24 | } catch (e) { 25 | console.error('ERROR query:', e); 26 | setStatus({ code: 1, message: e.toString() }); 27 | } 28 | }; 29 | 30 | const transaction = async () => { 31 | const { 32 | address, 33 | meta: { source, isInjected } 34 | } = accountPair; 35 | let fromParam; 36 | 37 | // set the signer 38 | if (isInjected) { 39 | const injected = await web3FromSource(source); 40 | fromParam = address; 41 | api.setSigner(injected.signer); 42 | } else { 43 | fromParam = accountPair; 44 | } 45 | 46 | let txExecute; 47 | try { 48 | // Check if tx has params 49 | if (!params) { 50 | txExecute = !sudo ? tx() : tx.sudo(); 51 | } else { 52 | txExecute = !sudo ? tx(...params) : tx.sudo(...params); 53 | } 54 | } catch (e) { 55 | console.error('ERROR forming transaction:', e); 56 | setStatus({ code: 1, message: e.toString() }); 57 | } 58 | 59 | if (txExecute) { 60 | txExecute 61 | .signAndSend(fromParam, ({ events = [], status }) => { 62 | status.isFinalized 63 | ? setStatus( 64 | { code: 0, message: `Transaction completed at block ${status.asFinalized.toString()}`, events: events } 65 | ) 66 | : setStatus({ code: null, message: `Transaction status: ${status.type}` }); 67 | }) 68 | .catch(e => { 69 | setStatus({ code: 1, message: `Transaction failed: ${e.toString()}` }); 70 | console.error('ERROR transaction:', e); 71 | }); 72 | } 73 | }; 74 | 75 | const onClick = () => { 76 | setStatus({ code: -1, message: 'Sending...' }); 77 | isQuery() ? query() : transaction(); 78 | }; 79 | 80 | return ( 81 | 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/components/index.js: -------------------------------------------------------------------------------- 1 | import DeveloperConsole from './DeveloperConsole'; 2 | import TxButton from './TxButton'; 3 | 4 | export { DeveloperConsole, TxButton }; 5 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/index.js: -------------------------------------------------------------------------------- 1 | import useSubstrate from './useSubstrate'; 2 | import { 3 | SubstrateContext, SubstrateContextProvider 4 | } from './SubstrateContext'; 5 | import utils from './utils'; 6 | 7 | export { useSubstrate, SubstrateContext, SubstrateContextProvider, utils }; 8 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/useSubstrate.js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useCallback } from 'react'; 2 | import { ApiPromise, WsProvider } from '@polkadot/api'; 3 | import { web3Accounts, web3Enable } from '@polkadot/extension-dapp'; 4 | import keyring from '@polkadot/ui-keyring'; 5 | 6 | import config from '../config'; 7 | import { SubstrateContext } from './SubstrateContext'; 8 | 9 | const useSubstrate = () => { 10 | const [state, dispatch] = useContext(SubstrateContext); 11 | 12 | // `useCallback` so that returning memoized function and not created 13 | // everytime, and thus re-render. 14 | const { api, socket, types } = state; 15 | const connect = useCallback(async () => { 16 | if (api) return; 17 | 18 | const provider = new WsProvider(socket); 19 | 20 | try { 21 | const _api = await ApiPromise.create({ provider, types }); 22 | dispatch({ type: 'CONNECT', payload: _api }); 23 | await _api.isReady; 24 | dispatch({ type: 'CONNECT_SUCCESS' }); 25 | } catch (e) { 26 | console.error(e); 27 | dispatch({ type: 'CONNECT_ERROR' }); 28 | } 29 | }, [api, socket, types, dispatch]); 30 | 31 | // hook to get injected accounts 32 | const { keyringState } = state; 33 | const loadAccounts = useCallback(async () => { 34 | // Ensure the method only run once. 35 | if (keyringState) return; 36 | 37 | try { 38 | await web3Enable(config.APP_NAME); 39 | let allAccounts = await web3Accounts(); 40 | allAccounts = allAccounts.map(({ address, meta }) => 41 | ({ address, meta: { ...meta, name: `${meta.name} (${meta.source})` } })); 42 | 43 | keyring.loadAll({ isDevelopment: config.DEVELOPMENT_KEYRING }, allAccounts); 44 | dispatch({ type: 'SET_KEYRING', payload: keyring }); 45 | } catch (e) { 46 | console.error(e); 47 | dispatch({ type: 'KEYRING_ERROR' }); 48 | } 49 | }, [keyringState, dispatch]); 50 | 51 | useEffect(() => { 52 | connect(); 53 | }, [connect]); 54 | 55 | useEffect(() => { 56 | loadAccounts(); 57 | }, [loadAccounts]); 58 | 59 | return { ...state, dispatch }; 60 | }; 61 | 62 | export default useSubstrate; 63 | -------------------------------------------------------------------------------- /ui/src/substrate-lib/utils.js: -------------------------------------------------------------------------------- 1 | import { blake2AsHex } from '@polkadot/util-crypto'; 2 | 3 | import BigJS from 'big.js'; 4 | import toFormat from 'toformat'; 5 | 6 | const Big = toFormat(BigJS); 7 | 8 | const utils = { 9 | prettyBalance: function (amt, opts = {}) { 10 | if (typeof amt !== 'number' && typeof amt !== 'string') { 11 | throw new Error(`${amt} is not a number`); 12 | } 13 | 14 | // default option values 15 | opts = { power: 8, decimal: 2, unit: 'Units', ...opts }; 16 | 17 | const bn = Big(amt); 18 | const divisor = Big(10).pow(opts.power); 19 | const displayed = bn.div(divisor).toFormat(opts.decimal); 20 | return `${displayed.toString()} ${opts.unit}`; 21 | }, 22 | 23 | getEVMAccountID: function (address) { 24 | return `0x${blake2AsHex(address, 256).substring(26)}`; 25 | } 26 | }; 27 | 28 | export default utils; 29 | -------------------------------------------------------------------------------- /utils/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | # Substrate EVM Utilities 2 | 3 | This directory is home to a Node.js project with some helpful utilities for working with Substrate and the EVM pallet. 4 | 5 | ## Installation and Usage 6 | 7 | Use `npm i` to install dependencies. To use these utilities, execute `node ./utils ` in the project root (i.e. the parent of this folder). 8 | 9 | ## Commands 10 | 11 | This utility supports the following commands: 12 | 13 | ### `--erc20-slot
` 14 | 15 | Calculate the storage slot for an (EVM) address's ERC-20 balance, where `` is the storage slot for the ERC-20 balances map 16 | 17 | ``` 18 | $ node ./utils --erc20-slot 0 0x57d213d0927ccc7596044c6ba013dd05522aacba 19 | $ 0xa7473b24b6fd8e15602cfb2f15c6a2e2770a692290d0c5097b77dd334132b7ce 20 | ``` 21 | 22 | ### `--evm-address
` 23 | 24 | Calculate the EVM address that corresponds to a native Substrate address. 25 | 26 | ``` 27 | $ node ./utils --evm-address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 28 | $ 0x57d213d0927ccc7596044c6ba013dd05522aacba 29 | ``` 30 | 31 | ### `---help` 32 | 33 | Print a help message for the utility project. 34 | -------------------------------------------------------------------------------- /utils/erc20-slot.js: -------------------------------------------------------------------------------- 1 | const help = `--erc20-slot
: Calculate the storage slot for an (EVM) address's ERC-20 balance, where is the storage slot for the ERC-20 balances map.`; 2 | 3 | module.exports = () => { 4 | if (process.argv.length < 5) { 5 | console.error('Please provide both the and
parameters.'); 6 | console.error(help); 7 | process.exit(9); 8 | } 9 | 10 | const slot = process.argv[3]; 11 | const address = process.argv[4]; 12 | if (!address.match(/^0x[a-f0-9]{40}$/)) { 13 | console.error('Please enter a valid EVM address.'); 14 | console.error(help); 15 | process.exit(9); 16 | } 17 | 18 | const mapStorageSlot = slot.padStart(64, '0'); 19 | const mapKey = address.substring(2).padStart(64, '0'); 20 | 21 | const web3 = require('web3'); 22 | return web3.utils.sha3('0x'.concat(mapKey.concat(mapStorageSlot))); 23 | }; 24 | -------------------------------------------------------------------------------- /utils/evm-address.js: -------------------------------------------------------------------------------- 1 | const help = `--evm-address
: Calculate the EVM address that corresponds to a native Substrate address.`; 2 | 3 | module.exports = () => { 4 | if (process.argv.length < 4) { 5 | console.error('Please provide the
parameter.'); 6 | console.error(help); 7 | process.exit(9); 8 | } 9 | 10 | const address = process.argv[3]; 11 | if (!address.match(/^[A-z0-9]{48}$/)) { 12 | console.error('Please enter a valid Substrate address.'); 13 | console.error(help); 14 | process.exit(9); 15 | } 16 | 17 | const crypto = require('@polkadot/util-crypto'); 18 | return `0x${crypto.blake2AsHex(crypto.decodeAddress(address), 256).substring(26)}`; 19 | }; 20 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | const help = 2 | `--erc20-slot
: Calculate the storage slot for an (EVM) address's ERC-20 balance, where is the storage slot for the ERC-20 balances map. 3 | --evm-address
: Calculate the EVM address that corresponds to a native Substrate address. 4 | --help: Print this message.`; 5 | 6 | if (process.argv.length < 3) { 7 | console.error('Please provide a command.'); 8 | console.error(help); 9 | process.exit(9); 10 | } 11 | 12 | const command = process.argv[2]; 13 | switch (command) { 14 | case "--erc20-slot": 15 | console.log(require('./erc20-slot')()); 16 | break; 17 | case "--evm-address": 18 | console.log(require('./evm-address')()); 19 | break; 20 | case "--help": 21 | console.log(help); 22 | break; 23 | default: 24 | console.error(`Unrecognized command: ${command}.`); 25 | console.error(help); 26 | process.exit(9); 27 | } 28 | -------------------------------------------------------------------------------- /utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "substrate-evm-utils", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@polkadot/util-crypto": "^2.2.1", 6 | "keccak": "^3.0.0", 7 | "web3": "^1.2.6" 8 | } 9 | } 10 | --------------------------------------------------------------------------------