├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── fm-demo ├── README.md ├── adapter.env ├── chainlink.env ├── checkAnswer.js ├── docker-compose.yml ├── docker-init-scripts │ └── postgres │ │ └── create-multiple-databases.sh ├── feedSetup.js ├── feeds.json ├── go-scripts │ ├── .github │ │ └── workflows │ │ │ ├── lint.yaml │ │ │ └── test.yaml │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── client │ │ ├── blockchain.go │ │ ├── blockchain_test.go │ │ ├── chainlink.go │ │ ├── chainlink_models.go │ │ ├── chainlink_test.go │ │ ├── client_suite_test.go │ │ ├── ethereum.go │ │ └── ethereum_test.go │ ├── config │ │ ├── config.go │ │ ├── config.yml │ │ └── ocr_config.json │ ├── contracts │ │ ├── contract_models.go │ │ ├── contract_suite_test.go │ │ ├── contracts_test.go │ │ ├── ethereum │ │ │ ├── FluxAggregator.go │ │ │ ├── LinkToken.go │ │ │ ├── OffchainAggregator.go │ │ │ ├── VRF.go │ │ │ ├── store.go │ │ │ ├── v0.4 │ │ │ │ ├── abi │ │ │ │ │ ├── BasicToken.abi │ │ │ │ │ ├── ERC20.abi │ │ │ │ │ ├── ERC20Basic.abi │ │ │ │ │ ├── ERC677.abi │ │ │ │ │ ├── ERC677Receiver.abi │ │ │ │ │ ├── ERC677Token.abi │ │ │ │ │ ├── LinkToken.abi │ │ │ │ │ ├── SafeMathChainlink.abi │ │ │ │ │ └── StandardToken.abi │ │ │ │ ├── bin │ │ │ │ │ ├── BasicToken.bin │ │ │ │ │ ├── ERC20.bin │ │ │ │ │ ├── ERC20Basic.bin │ │ │ │ │ ├── ERC677.bin │ │ │ │ │ ├── ERC677Receiver.bin │ │ │ │ │ ├── ERC677Token.bin │ │ │ │ │ ├── LinkToken.bin │ │ │ │ │ ├── SafeMathChainlink.bin │ │ │ │ │ └── StandardToken.bin │ │ │ │ └── src │ │ │ │ │ ├── ERC677Token.sol │ │ │ │ │ ├── LinkToken.sol │ │ │ │ │ ├── interfaces │ │ │ │ │ ├── ERC20.sol │ │ │ │ │ ├── ERC20Basic.sol │ │ │ │ │ ├── ERC677.sol │ │ │ │ │ └── ERC677Receiver.sol │ │ │ │ │ └── vendor │ │ │ │ │ ├── BasicToken.sol │ │ │ │ │ ├── SafeMathChainlink.sol │ │ │ │ │ └── StandardToken.sol │ │ │ ├── v0.6 │ │ │ │ ├── abi │ │ │ │ │ ├── AggregatorInterface.abi │ │ │ │ │ ├── AggregatorV2V3Interface.abi │ │ │ │ │ ├── AggregatorV3Interface.abi │ │ │ │ │ ├── AggregatorValidatorInterface.abi │ │ │ │ │ ├── FluxAggregator.abi │ │ │ │ │ ├── LinkTokenInterface.abi │ │ │ │ │ ├── Median.abi │ │ │ │ │ ├── Owned.abi │ │ │ │ │ ├── SafeMath128.abi │ │ │ │ │ ├── SafeMath32.abi │ │ │ │ │ ├── SafeMath64.abi │ │ │ │ │ ├── SafeMathChainlink.abi │ │ │ │ │ ├── SignedSafeMath.abi │ │ │ │ │ └── VRF.abi │ │ │ │ ├── bin │ │ │ │ │ ├── AggregatorInterface.bin │ │ │ │ │ ├── AggregatorV2V3Interface.bin │ │ │ │ │ ├── AggregatorV3Interface.bin │ │ │ │ │ ├── AggregatorValidatorInterface.bin │ │ │ │ │ ├── FluxAggregator.bin │ │ │ │ │ ├── LinkTokenInterface.bin │ │ │ │ │ ├── Median.bin │ │ │ │ │ ├── Owned.bin │ │ │ │ │ ├── SafeMath128.bin │ │ │ │ │ ├── SafeMath32.bin │ │ │ │ │ ├── SafeMath64.bin │ │ │ │ │ ├── SafeMathChainlink.bin │ │ │ │ │ ├── SignedSafeMath.bin │ │ │ │ │ └── VRF.bin │ │ │ │ └── src │ │ │ │ │ ├── FluxAggregator.sol │ │ │ │ │ ├── Median.sol │ │ │ │ │ ├── Owned.sol │ │ │ │ │ ├── SafeMath128.sol │ │ │ │ │ ├── SafeMath32.sol │ │ │ │ │ ├── SafeMath64.sol │ │ │ │ │ ├── SignedSafeMath.sol │ │ │ │ │ ├── VRF.sol │ │ │ │ │ ├── interfaces │ │ │ │ │ ├── AggregatorInterface.sol │ │ │ │ │ ├── AggregatorV2V3Interface.sol │ │ │ │ │ ├── AggregatorV3Interface.sol │ │ │ │ │ ├── AggregatorValidatorInterface.sol │ │ │ │ │ └── LinkTokenInterface.sol │ │ │ │ │ └── vendor │ │ │ │ │ └── SafeMathChainlink.sol │ │ │ └── v0.7 │ │ │ │ ├── abi │ │ │ │ ├── AccessControllerInterface.abi │ │ │ │ ├── AggregatorInterface.abi │ │ │ │ ├── AggregatorV2V3Interface.abi │ │ │ │ ├── AggregatorV3Interface.abi │ │ │ │ ├── AggregatorValidatorInterface.abi │ │ │ │ ├── LinkTokenInterface.abi │ │ │ │ ├── OffchainAggregator.abi │ │ │ │ ├── OffchainAggregatorBilling.abi │ │ │ │ ├── Owned.abi │ │ │ │ └── TypeAndVersionInterface.abi │ │ │ │ ├── bin │ │ │ │ ├── AccessControllerInterface.bin │ │ │ │ ├── AggregatorInterface.bin │ │ │ │ ├── AggregatorV2V3Interface.bin │ │ │ │ ├── AggregatorV3Interface.bin │ │ │ │ ├── AggregatorValidatorInterface.bin │ │ │ │ ├── LinkTokenInterface.bin │ │ │ │ ├── OffchainAggregator.bin │ │ │ │ ├── OffchainAggregatorBilling.bin │ │ │ │ ├── Owned.bin │ │ │ │ └── TypeAndVersionInterface.bin │ │ │ │ └── src │ │ │ │ ├── AccessControllerInterface.sol │ │ │ │ ├── AggregatorInterface.sol │ │ │ │ ├── AggregatorV2V3Interface.sol │ │ │ │ ├── AggregatorV3Interface.sol │ │ │ │ ├── AggregatorValidatorInterface.sol │ │ │ │ ├── LinkTokenInterface.sol │ │ │ │ ├── OffchainAggregator.sol │ │ │ │ ├── OffchainAggregatorBilling.sol │ │ │ │ ├── Owned.sol │ │ │ │ └── TypeAndVersionInterface.sol │ │ └── ethereum_contracts.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ ├── specs │ │ └── spec.json │ └── tools │ │ ├── README.md │ │ ├── compile_contracts.py │ │ └── tools.go ├── package.json ├── scripts │ ├── add-bridges.sh │ ├── add-jobspecs.sh │ ├── common.sh │ ├── ei-config.sh │ ├── run-all.sh │ ├── run-chain.sh │ ├── run-chainlink.sh │ └── run-ei.sh ├── secrets │ ├── apicredentials │ └── password.txt └── specs │ ├── spec.json │ └── spec2.json ├── pallet-chainlink-feed ├── Cargo.lock ├── Cargo.toml ├── README.md └── src │ ├── benchmarking.rs │ ├── default_weights.rs │ ├── feed.rs │ ├── lib.rs │ ├── mock.rs │ ├── tests.rs │ ├── traits.rs │ └── utils.rs ├── pallet-chainlink ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── lib.rs │ └── tests.rs ├── runlog-demo ├── .gitignore ├── README.md ├── adapter.env ├── chainlink.env ├── docker-compose.yml ├── docker-init-scripts │ └── create-multiple-databases.sh ├── internal-scripts │ ├── add-bridge.sh │ ├── add-ei.sh │ ├── add-jobspec.sh │ └── common.sh ├── operatorSetup.js ├── package-lock.json ├── package.json ├── secrets │ ├── apicredentials │ └── password.txt └── setup ├── rustfmt.toml └── substrate-node-example ├── .devcontainer └── devcontainer.json ├── .github ├── ISSUE_TEMPLATE │ ├── ask-a-question.md │ ├── report-a-bug.md │ └── suggest-a-feature.md └── workflows │ └── build-push-template.yml ├── .gitignore ├── .vscode └── tasks.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── doc └── rust-setup.md ├── docker-compose.yml ├── front-end ├── .circleci │ └── config.yml ├── .env ├── .gitignore ├── .nvmrc ├── .yarn │ ├── plugins │ │ └── @yarnpkg │ │ │ └── plugin-interactive-tools.cjs │ └── releases │ │ └── yarn-berry.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── config-overrides.js ├── package.json ├── public │ ├── assets │ │ ├── favicon.ico │ │ ├── main.css │ │ └── substrate-logo.png │ ├── index.html │ └── manifest.json └── src │ ├── AccountSelector.js │ ├── App.js │ ├── Balances.js │ ├── BlockNumber.js │ ├── ChainlinkExample.js │ ├── Events.js │ ├── Interactor.js │ ├── Metadata.js │ ├── NodeInfo.js │ ├── TemplateModule.js │ ├── Transfer.js │ ├── Upgrade.js │ ├── __tests__ │ ├── App.js │ └── mock.js │ ├── config │ ├── common.json │ ├── development.json │ ├── index.js │ ├── production.json │ ├── test.json │ └── types.json │ ├── index.js │ └── substrate-lib │ ├── SubstrateContext.js │ ├── components │ ├── DeveloperConsole.js │ ├── TxButton.js │ └── index.js │ ├── index.js │ └── utils.js ├── mock-operator ├── README.md ├── index.js └── package.json ├── node ├── Cargo.toml ├── build.rs └── src │ ├── chain_spec.rs │ ├── cli.rs │ ├── command.rs │ ├── lib.rs │ ├── main.rs │ ├── rpc.rs │ └── service.rs ├── pallets └── template │ ├── Cargo.toml │ └── src │ ├── benchmarking.rs │ ├── lib.rs │ ├── mock.rs │ └── tests.rs ├── runtime ├── Cargo.toml ├── build.rs └── src │ ├── example.rs │ ├── lib.rs │ └── weights │ ├── mod.rs │ └── pallet_chainlink_feed.rs ├── scripts ├── docker_run.sh └── init.sh ├── specs ├── README.md ├── chainlink-feed.json ├── chainlink-no-admin-funds.json ├── chainlink.json └── feedconfig.json └── types.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*.rs] 3 | indent_style=tab 4 | indent_size=tab 5 | tab_width=4 6 | end_of_line=lf 7 | charset=utf-8 8 | trim_trailing_whitespace=true 9 | max_line_length=100 10 | insert_final_newline=true 11 | 12 | [*.yml] 13 | indent_style=space 14 | indent_size=2 15 | tab_width=8 16 | end_of_line=lf 17 | 18 | [*.sh] 19 | indent_style=space 20 | indent_size=2 21 | tab_width=8 22 | end_of_line=lf 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/*.rs.bk 3 | **/node_modules 4 | **/yarn.lock 5 | external_initiator.env 6 | cookiefile 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL=help 2 | 3 | # Show this help. 4 | help: 5 | @awk '/^#/{c=substr($$0,3);next}c&&/^[[:alpha:]][[:print:]]+:/{print substr($$1,1,index($$1,":")),c}1{c=0}' $(MAKEFILE_LIST) | column -s: -t 6 | 7 | # Run the example chain 8 | run-temp: 9 | cd substrate-node-example; cargo run --release -- --dev --tmp 10 | 11 | run-chain: 12 | cd substrate-node-example; cargo run --release -- --dev --ws-external --rpc-external 13 | 14 | purge-chain: 15 | cd substrate-node-example; cargo run --release -- purge-chain --dev 16 | 17 | run-front-end: 18 | cd substrate-node-example/front-end; yarn start -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chainlink-polkadot 2 | 3 | This repository contains the [Chainlink](https://chain.link/) feed pallet as well as an example node showing how to integrate 4 | it in [Substrate](https://www.substrate.io/)-based chains. 5 | 6 | It also includes the `pallet-chainlink` for interacting with the Chainlink job-based oracle system. 7 | 8 | ## How to integrate the Chainlink feed pallet into a runtime? 9 | The pallet is added to the runtime like any regular pallet (see [tutorial](https://substrate.dev/docs/en/tutorials/add-a-pallet/)). 10 | It then needs to be configured. See the [pallet readme](./pallet-chainlink-feed/README.md) for details. 11 | 12 | The usage is simple: 13 | ```Rust 14 | let feed = T::Oracle::feed(0.into()).ok_or(Error::::FeedMissing)?; 15 | let RoundData { answer, .. } = feed.latest_data(); 16 | do_something_with_answer(answer); 17 | ``` 18 | See [the template pallet](./substrate-node-example/pallets/template/src/lib.rs) for a full example showing how to access a price feed. 19 | 20 | 21 | ## Run the example 22 | 23 | `substrate-node-example` demonstrates how to use `pallet-chainlink-feed` end-to-end. 24 | To test: 25 | 26 | * start the chain using `make run-temp` (for a temporary node which cleans up after itself) 27 | * connect to the chain by pointing https://polkadot.js.org/apps/ (or a locally hosted version) to the local dev node 28 | * specify the types by copying `substrate-node-example/types.json` into the input at `Settings > Developer` 29 | 30 | You are now ready to send extrinsics to the pallet. 31 | 32 | -------------------------------------------------------------------------------- /fm-demo/adapter.env: -------------------------------------------------------------------------------- 1 | SA_TX_TYPE=immortal 2 | SA_ENDPOINT=ws://chain:9944/ 3 | -------------------------------------------------------------------------------- /fm-demo/chainlink.env: -------------------------------------------------------------------------------- 1 | ROOT=/chainlink 2 | LOG_LEVEL=debug 3 | ETH_CHAIN_ID=1 4 | MIN_OUTGOING_CONFIRMATIONS=2 5 | CHAINLINK_TLS_PORT=0 6 | SECURE_COOKIES=false 7 | ALLOW_ORIGINS=* 8 | CHAINLINK_DEV=true 9 | ETH_DISABLED=true 10 | -------------------------------------------------------------------------------- /fm-demo/checkAnswer.js: -------------------------------------------------------------------------------- 1 | const {ApiPromise, Keyring, WsProvider} = require('@polkadot/api'); 2 | const {cryptoWaitReady} = require('@polkadot/util-crypto'); 3 | const feedConfigs = require('./feeds.json'); 4 | const types = require('../substrate-node-example/types.json'); 5 | 6 | async function main() { 7 | await cryptoWaitReady(); 8 | 9 | // Connect to the local chain 10 | const wsProvider = new WsProvider('ws://localhost:9944'); 11 | const api = await ApiPromise.create({ 12 | provider: wsProvider, 13 | types 14 | }); 15 | for (feedId = 0; feedId < feedConfigs.length; feedId++) { 16 | console.log(`Feed ID: ${feedId}`) 17 | const feed = (await api.query.chainlinkFeed.feeds(feedId)).unwrap() 18 | 19 | const latestRoundNumber = feed['latest_round'].words[0] 20 | 21 | const latestRound = (await api.query.chainlinkFeed.rounds(0, latestRoundNumber)).unwrap() 22 | 23 | const latestAnswer = latestRound.answer.unwrap().toNumber() 24 | console.log("Latest answer on chain: ") 25 | console.log(latestAnswer) 26 | } 27 | 28 | } 29 | 30 | main().catch(console.error).then(() => process.exit()); 31 | -------------------------------------------------------------------------------- /fm-demo/docker-init-scripts/postgres/create-multiple-databases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -u 5 | 6 | function create_user_and_database() { 7 | local database=$1 8 | echo " Creating user and database '$database'" 9 | psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL 10 | CREATE USER $database; 11 | CREATE DATABASE $database; 12 | GRANT ALL PRIVILEGES ON DATABASE $database TO $database; 13 | EOSQL 14 | } 15 | 16 | if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then 17 | echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" 18 | for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do 19 | create_user_and_database $db 20 | done 21 | echo "Multiple databases created" 22 | fi 23 | -------------------------------------------------------------------------------- /fm-demo/feeds.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "operatorSeedPhrase": "fruit start corn kingdom leg public thumb scrub negative jazz pig critic volcano voice suspect", 4 | "payment" : 0, 5 | "timeout" : 600, 6 | "submissionValueBounds": ["0", "99999999999999999999999999999999"], 7 | "minSubmissions" : 1, 8 | "decimals" : 8, 9 | "description" : "Test", 10 | "restartDelay": 0, 11 | "oracles": ["0x7c522c8273973e7bcf4a5dbfcc745dba4a3ab08c1e410167d7b1bdf9cb924f6c", "0x06f0d58c43477508c0e5d5901342acf93a0208088816ff303996564a1d8c1c54"], 12 | "pruningWindow" : 56, 13 | "maxDebt" : 1 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | - main 9 | pull_request: 10 | jobs: 11 | golangci: 12 | name: lint 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: golangci-lint 17 | uses: golangci/golangci-lint-action@v2 18 | with: 19 | args: --exclude-use-default=false --skip-dirs=contracts/ethereum 20 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | - main 9 | pull_request: 10 | jobs: 11 | test: 12 | strategy: 13 | matrix: 14 | go-version: [1.16.x] 15 | os: [ubuntu-latest] 16 | runs-on: ${{ matrix.os }} 17 | services: 18 | hardhat: 19 | image: smartcontract/hardhat-network 20 | ports: 21 | - 8545:8545 22 | options: >- 23 | --health-cmd "curl -X POST --data '{"jsonrpc":"2.0","method":"net_listening","params":[],"id":31337}' http://localhost:8545" 24 | --health-interval 10s 25 | --health-timeout 5s 26 | --health-retries 5 27 | steps: 28 | - name: Install Go 29 | uses: actions/setup-go@v2 30 | with: 31 | go-version: ${{ matrix.go-version }} 32 | - name: Checkout code 33 | uses: actions/checkout@v2 34 | - name: Test 35 | run: go test ./... -v -covermode=count -coverprofile=coverage.out 36 | - name: Convert coverage to lcov 37 | uses: jandelgado/gcov2lcov-action@v1.0.5 38 | - name: Report code coverage 39 | uses: romeovs/lcov-reporter-action@v0.2.16 40 | with: 41 | github-token: ${{ secrets.GITHUB_TOKEN }} 42 | lcov-file: ./coverage.lcov 43 | - name: Publish Unit Test Results 44 | uses: mikepenz/action-junit-report@v2 45 | if: always() 46 | with: 47 | report_paths: '**/junit.xml' 48 | github_token: ${{ secrets.GITHUB_TOKEN }} 49 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Hardhat config that might sneak through 4 | node_modules/ 5 | hardhat.config.js 6 | package-lock.json 7 | package.json 8 | artifacts/ 9 | 10 | # Binaries for programs and plugins 11 | *.exe 12 | *.exe~ 13 | *.dll 14 | *.so 15 | *.dylib 16 | 17 | # Test binary, built with `go test -c` 18 | *.test 19 | 20 | # Output of the go coverage tool, specifically when used with LiteIDE 21 | *.out 22 | 23 | # Dependency directories (remove the comment below to include it) 24 | # vendor/ 25 | 26 | # JUnit Test Reports 27 | junit.xml 28 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SmartContract 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/README.md: -------------------------------------------------------------------------------- 1 | # Chainlink Integration Framework 2 | 3 | ![Tests](https://github.com/smartcontractkit/integrations-framework/actions/workflows/test.yaml/badge.svg) 4 | ![Lint](https://github.com/smartcontractkit/integrations-framework/actions/workflows/lint.yaml/badge.svg) 5 | 6 | A framework for interacting with chainlink nodes, environments, and other blockchain systems. The framework is primarilly intended to facillitate testing chainlink features and stability. 7 | 8 | ## How to Test 9 | 10 | 1. Start a local hardhat network. You can easily do so by using our [docker container](https://hub.docker.com/r/smartcontract/hardhat-network). You could also deploy [your own local version](https://hardhat.org/hardhat-network/), if you are so inclined. 11 | 2. Run `go test ./...` 12 | 13 | ## // TODO 14 | 15 | * Add more chainlink node checks 16 | * Enable connecting chainlink node interfaces to actual running nodes in an environment 17 | * Check out [hardhat deploy](https://hardhat.org/plugins/hardhat-deploy.html) to help setup test environments 18 | * Look into [precompiling tests with Ginkgo](https://onsi.github.io/ginkgo/#precompiling-tests) to speed up test execution 19 | * Implement `Ginkgo` CLI into tests, ideally with minimal impact to local test users. This allows us to use some cool stuff like [precompiling tests](https://onsi.github.io/ginkgo/#precompiling-tests), and [running in parallel](https://onsi.github.io/ginkgo/#parallel-specs) 20 | 21 | Check out our [clubhouse board](https://app.clubhouse.io/chainlinklabs/project/5690/qa-team?vc_group_by=day) for a look into our progress. 22 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/client/blockchain_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "integrations-framework/config" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/ginkgo/extensions/table" 8 | . "github.com/onsi/gomega" 9 | ) 10 | 11 | var _ = Describe("Client", func() { 12 | var conf *config.Config 13 | 14 | BeforeEach(func() { 15 | var err error 16 | conf, err = config.NewWithPath(config.LocalConfig, "../config") 17 | Expect(err).ShouldNot(HaveOccurred()) 18 | }) 19 | 20 | DescribeTable("create new wallet configurations", func( 21 | initFunc BlockchainNetworkInit, 22 | privateKeyString string, 23 | address string, 24 | ) { 25 | networkConfig, err := initFunc(conf) 26 | Expect(err).ShouldNot(HaveOccurred()) 27 | wallets, err := networkConfig.Wallets() 28 | Expect(err).ShouldNot(HaveOccurred()) 29 | Expect(wallets.Default().PrivateKey()).To(Equal(privateKeyString)) 30 | Expect(address).To(Equal(wallets.Default().Address())) 31 | }, 32 | Entry("on Ethereum Hardhat", NewHardhatNetwork, 33 | "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", 34 | "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), 35 | Entry("on Ethereum Kovan", NewKovanNetwork, 36 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 37 | "0x8fd379246834eac74B8419FfdA202CF8051F7A03"), 38 | Entry("on Ethereum Goerli", NewGoerliNetwork, 39 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 40 | "0x8fd379246834eac74B8419FfdA202CF8051F7A03"), 41 | ) 42 | }) 43 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/client/client_suite_test.go: -------------------------------------------------------------------------------- 1 | package client_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/onsi/ginkgo/reporters" 8 | "github.com/rs/zerolog" 9 | "github.com/rs/zerolog/log" 10 | 11 | . "github.com/onsi/ginkgo" 12 | . "github.com/onsi/gomega" 13 | ) 14 | 15 | func TestClient(t *testing.T) { 16 | RegisterFailHandler(Fail) 17 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) 18 | 19 | junitReporter := reporters.NewJUnitReporter("junit.xml") 20 | RunSpecsWithCustomReporters(t, "Client Suite", []Reporter{junitReporter}) 21 | } 22 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/client/ethereum_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "integrations-framework/config" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/ginkgo/extensions/table" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | var _ = Describe("Ethereum functionality", func() { 15 | var conf *config.Config 16 | 17 | BeforeEach(func() { 18 | var err error 19 | conf, err = config.NewWithPath(config.LocalConfig, "../config") 20 | Expect(err).ShouldNot(HaveOccurred()) 21 | }) 22 | 23 | DescribeTable("eth transaction basics", func( 24 | initFunc BlockchainNetworkInit, 25 | ) { 26 | // Setup 27 | networkConfig, err := initFunc(conf) 28 | Expect(err).ShouldNot(HaveOccurred()) 29 | client, err := NewEthereumClient(networkConfig) 30 | Expect(err).ShouldNot(HaveOccurred()) 31 | wallets, err := networkConfig.Wallets() 32 | Expect(err).ShouldNot(HaveOccurred()) 33 | 34 | // Actual transaction 35 | toWallet, err := wallets.Wallet(1) 36 | Expect(err).ShouldNot(HaveOccurred()) 37 | _, err = client.SendTransaction( 38 | wallets.Default(), 39 | common.HexToAddress(toWallet.Address()), 40 | big.NewInt(0), 41 | nil, 42 | ) 43 | Expect(err).ShouldNot(HaveOccurred()) 44 | }, 45 | Entry("on Ethereum Hardhat", NewHardhatNetwork), 46 | ) 47 | }) 48 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/config/config.yml: -------------------------------------------------------------------------------- 1 | # Retains default and configurable name-values for the integration framework 2 | # Names reference the blockchain that they store information for 3 | 4 | networks: 5 | ethereum_hardhat: 6 | name: "Ethereum Hardhat" 7 | url: "ws://localhost:8545" 8 | chain_id: 31337 9 | private_keys: 10 | - ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 11 | - 59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d 12 | - 5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a 13 | - 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6 14 | - 47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a 15 | - 8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba 16 | - 92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e 17 | - 4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356 18 | - dbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 19 | - 2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 20 | - f214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897 21 | - 701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82 22 | - a267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1 23 | - 47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd 24 | - c526ee95bf44d8fc405a158bb884d9d1238d99f0612e9f33d006bb0789009aaa 25 | - 8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61 26 | - ea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0 27 | - 689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd 28 | - de9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0 29 | - df57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e 30 | transaction_limit: 9500000 31 | transaction_timeout: 0 32 | minimum_confirmations: 0 33 | gas_estimation_buffer: 0 34 | ethereum_kovan: 35 | name: "Ethereum Kovan" 36 | url: "wss://parity-kovan.eth.devnet.tools/ws" 37 | chain_id: 42 38 | private_keys: 39 | - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 40 | transaction_limit: 1000000 41 | transaction_timeout: 5m 42 | minimum_confirmations: 2 43 | gas_estimation_buffer: 10000 44 | ethereum_goerli: 45 | name: "Ethereum Goerli" 46 | url: "wss://geth-goerli.eth.devnet.tools/ws" 47 | chain_id: 5 48 | private_keys: 49 | - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 50 | transaction_limit: 1000000 51 | transaction_timeout: 5m 52 | minimum_confirmations: 2 53 | gas_estimation_buffer: 10000 54 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/config/ocr_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "0xFb3099346A724CFa1d45aF598Ae42A38B226092e": { 3 | "DeltaProgress": "30s", 4 | "DeltaResend": "10s", 5 | "DeltaRound": "20s", 6 | "DeltaGrace": "1s", 7 | "DeltaC": "120s", 8 | "AlphaPPB": 1, 9 | "DeltaStage": "3s", 10 | "RMax": 4, 11 | "F": 1, 12 | "N": 5, 13 | "OracleIdentities": [ 14 | { 15 | "TransmitAddress": "0x36612b5F390a508db08a71AF04Bd8c874721Daab", 16 | "OnchainSigningAddress": "0x5960d6fab386716f201f3bc74ced7cfbf77674e5", 17 | "PeerID": "12D3KooWPoAJWBHK4KawkU7MURAqu6Jo4EbGCXgMeGL5nxr64wk6", 18 | "OffchainPublicKey": "0x2fec0c4aa2a39e4b5fb668390dc3d94f8cc1eedd712b42b6dce70cfb93168858", 19 | "ConfigPublicKey": "0x9ac68c2974afc4ef182258ef5d7e172e67ff66d126f625bf43e21ed3babb8f7b" 20 | }, 21 | { 22 | "TransmitAddress": "0xb7F7a3Bb00F1c40a9ffCED21B5FCDFC6283c043a", 23 | "OnchainSigningAddress": "0xd0c029f52c9483b2a4151001f13606e18a51a473", 24 | "PeerID": "12D3KooWPnbjw6EW5wszYJBUP8CLwq58RLR8yHeXhst9B7bMsUR2", 25 | "OffchainPublicKey": "0xc134a6241bcb05247bd6840d676d848467633751e2a82bd6413090dd33dea101", 26 | "ConfigPublicKey": "0xf9bf8f9bb51fb58ff293595b4dd13789453f02b4a95edbcf38ddc114bed9174c" 27 | }, 28 | { 29 | "TransmitAddress": "0x6bd1E4EC738bb262F97DC757f9D742745C72a3ce", 30 | "OnchainSigningAddress": "0x6b4226377589b4716d615ce1b7d74cea50d01606", 31 | "PeerID": "12D3KooWJpKqnjaJGktS5WsJhGCLfRC5y6xdLpeRJrgY2v4wNRXq", 32 | "OffchainPublicKey": "0x36fba78cc15abb03d24cbcf7b3f9c4e4644b2467c91c064e74f37e6123b3e1de", 33 | "ConfigPublicKey": "0x89059108a503480fa416d9bad24ccac97143893d7b9c13b9dd6a2462e83a6d25" 34 | }, 35 | { 36 | "TransmitAddress": "0x7413AE1eEE71FBe47178e4fb2D266E8B691bb4A2", 37 | "OnchainSigningAddress": "0x1fb66e23c3d0953138c1770c625042602f69d63d", 38 | "PeerID": "12D3KooWQsWo9U2BMnTxUkkQEyZkq5yyPELVHqBB1BaFKZYk8bJM", 39 | "OffchainPublicKey": "0x9b42bccc73c402eb5a0da7bec36e9a29de99dd6704ed8d77ad77a1666606eb7b", 40 | "ConfigPublicKey": "0xac9e1ea1836aa63cb22ce8e17ca26d253100761b5f87fbfdbcaca95910e51273" 41 | }, 42 | { 43 | "TransmitAddress": "0x26E1060c72098939A5Dd050428456Ba377761264", 44 | "OnchainSigningAddress": "0xd7290e02ed2d9e010246c9c62abfaa79ea7f507b", 45 | "PeerID": "12D3KooWE7f8oxWvpphH88hrGSuTnVt74dt4RUd43eTTQWJYFqYz", 46 | "OffchainPublicKey": "0xfb34c17805fb61f8e5049b77be877d06e94953be9514fcf75403002152e0ab88", 47 | "ConfigPublicKey": "0xc68887511cfab987f9aa39518db81b7b6453fcf9e4d03df4e4c1ebb8b8972672" 48 | } 49 | ] 50 | } 51 | } -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/contract_suite_test.go: -------------------------------------------------------------------------------- 1 | package contracts_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "github.com/rs/zerolog" 10 | "github.com/rs/zerolog/log" 11 | ) 12 | 13 | func TestContracts(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) 16 | RunSpecs(t, "Contract Suite") 17 | } 18 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/abi/BasicToken.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "totalSupply", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "uint256" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": true, 18 | "inputs": [ 19 | { 20 | "name": "_owner", 21 | "type": "address" 22 | } 23 | ], 24 | "name": "balanceOf", 25 | "outputs": [ 26 | { 27 | "name": "balance", 28 | "type": "uint256" 29 | } 30 | ], 31 | "payable": false, 32 | "stateMutability": "view", 33 | "type": "function" 34 | }, 35 | { 36 | "constant": false, 37 | "inputs": [ 38 | { 39 | "name": "_to", 40 | "type": "address" 41 | }, 42 | { 43 | "name": "_value", 44 | "type": "uint256" 45 | } 46 | ], 47 | "name": "transfer", 48 | "outputs": [ 49 | { 50 | "name": "", 51 | "type": "bool" 52 | } 53 | ], 54 | "payable": false, 55 | "stateMutability": "nonpayable", 56 | "type": "function" 57 | }, 58 | { 59 | "anonymous": false, 60 | "inputs": [ 61 | { 62 | "indexed": true, 63 | "name": "from", 64 | "type": "address" 65 | }, 66 | { 67 | "indexed": true, 68 | "name": "to", 69 | "type": "address" 70 | }, 71 | { 72 | "indexed": false, 73 | "name": "value", 74 | "type": "uint256" 75 | } 76 | ], 77 | "name": "Transfer", 78 | "type": "event" 79 | } 80 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/abi/ERC20.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": false, 4 | "inputs": [ 5 | { 6 | "name": "spender", 7 | "type": "address" 8 | }, 9 | { 10 | "name": "value", 11 | "type": "uint256" 12 | } 13 | ], 14 | "name": "approve", 15 | "outputs": [ 16 | { 17 | "name": "", 18 | "type": "bool" 19 | } 20 | ], 21 | "payable": false, 22 | "stateMutability": "nonpayable", 23 | "type": "function" 24 | }, 25 | { 26 | "constant": true, 27 | "inputs": [], 28 | "name": "totalSupply", 29 | "outputs": [ 30 | { 31 | "name": "", 32 | "type": "uint256" 33 | } 34 | ], 35 | "payable": false, 36 | "stateMutability": "view", 37 | "type": "function" 38 | }, 39 | { 40 | "constant": false, 41 | "inputs": [ 42 | { 43 | "name": "from", 44 | "type": "address" 45 | }, 46 | { 47 | "name": "to", 48 | "type": "address" 49 | }, 50 | { 51 | "name": "value", 52 | "type": "uint256" 53 | } 54 | ], 55 | "name": "transferFrom", 56 | "outputs": [ 57 | { 58 | "name": "", 59 | "type": "bool" 60 | } 61 | ], 62 | "payable": false, 63 | "stateMutability": "nonpayable", 64 | "type": "function" 65 | }, 66 | { 67 | "constant": true, 68 | "inputs": [ 69 | { 70 | "name": "who", 71 | "type": "address" 72 | } 73 | ], 74 | "name": "balanceOf", 75 | "outputs": [ 76 | { 77 | "name": "", 78 | "type": "uint256" 79 | } 80 | ], 81 | "payable": false, 82 | "stateMutability": "view", 83 | "type": "function" 84 | }, 85 | { 86 | "constant": false, 87 | "inputs": [ 88 | { 89 | "name": "to", 90 | "type": "address" 91 | }, 92 | { 93 | "name": "value", 94 | "type": "uint256" 95 | } 96 | ], 97 | "name": "transfer", 98 | "outputs": [ 99 | { 100 | "name": "", 101 | "type": "bool" 102 | } 103 | ], 104 | "payable": false, 105 | "stateMutability": "nonpayable", 106 | "type": "function" 107 | }, 108 | { 109 | "constant": true, 110 | "inputs": [ 111 | { 112 | "name": "owner", 113 | "type": "address" 114 | }, 115 | { 116 | "name": "spender", 117 | "type": "address" 118 | } 119 | ], 120 | "name": "allowance", 121 | "outputs": [ 122 | { 123 | "name": "", 124 | "type": "uint256" 125 | } 126 | ], 127 | "payable": false, 128 | "stateMutability": "view", 129 | "type": "function" 130 | }, 131 | { 132 | "anonymous": false, 133 | "inputs": [ 134 | { 135 | "indexed": true, 136 | "name": "owner", 137 | "type": "address" 138 | }, 139 | { 140 | "indexed": true, 141 | "name": "spender", 142 | "type": "address" 143 | }, 144 | { 145 | "indexed": false, 146 | "name": "value", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "Approval", 151 | "type": "event" 152 | }, 153 | { 154 | "anonymous": false, 155 | "inputs": [ 156 | { 157 | "indexed": true, 158 | "name": "from", 159 | "type": "address" 160 | }, 161 | { 162 | "indexed": true, 163 | "name": "to", 164 | "type": "address" 165 | }, 166 | { 167 | "indexed": false, 168 | "name": "value", 169 | "type": "uint256" 170 | } 171 | ], 172 | "name": "Transfer", 173 | "type": "event" 174 | } 175 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/abi/ERC20Basic.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "totalSupply", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "uint256" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": true, 18 | "inputs": [ 19 | { 20 | "name": "who", 21 | "type": "address" 22 | } 23 | ], 24 | "name": "balanceOf", 25 | "outputs": [ 26 | { 27 | "name": "", 28 | "type": "uint256" 29 | } 30 | ], 31 | "payable": false, 32 | "stateMutability": "view", 33 | "type": "function" 34 | }, 35 | { 36 | "constant": false, 37 | "inputs": [ 38 | { 39 | "name": "to", 40 | "type": "address" 41 | }, 42 | { 43 | "name": "value", 44 | "type": "uint256" 45 | } 46 | ], 47 | "name": "transfer", 48 | "outputs": [ 49 | { 50 | "name": "", 51 | "type": "bool" 52 | } 53 | ], 54 | "payable": false, 55 | "stateMutability": "nonpayable", 56 | "type": "function" 57 | }, 58 | { 59 | "anonymous": false, 60 | "inputs": [ 61 | { 62 | "indexed": true, 63 | "name": "from", 64 | "type": "address" 65 | }, 66 | { 67 | "indexed": true, 68 | "name": "to", 69 | "type": "address" 70 | }, 71 | { 72 | "indexed": false, 73 | "name": "value", 74 | "type": "uint256" 75 | } 76 | ], 77 | "name": "Transfer", 78 | "type": "event" 79 | } 80 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/abi/ERC677Receiver.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": false, 4 | "inputs": [ 5 | { 6 | "name": "_sender", 7 | "type": "address" 8 | }, 9 | { 10 | "name": "_value", 11 | "type": "uint256" 12 | }, 13 | { 14 | "name": "_data", 15 | "type": "bytes" 16 | } 17 | ], 18 | "name": "onTokenTransfer", 19 | "outputs": [], 20 | "payable": false, 21 | "stateMutability": "nonpayable", 22 | "type": "function" 23 | } 24 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/abi/SafeMathChainlink.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/BasicToken.bin: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b5061023e806100206000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318160ddd811461005b57806370a0823114610082578063a9059cbb146100b0575b600080fd5b34801561006757600080fd5b506100706100f5565b60408051918252519081900360200190f35b34801561008e57600080fd5b5061007073ffffffffffffffffffffffffffffffffffffffff600435166100fb565b3480156100bc57600080fd5b506100e173ffffffffffffffffffffffffffffffffffffffff60043516602435610123565b604080519115158252519081900360200190f35b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b33600090815260016020526040812054610143908363ffffffff6101ed16565b336000908152600160205260408082209290925573ffffffffffffffffffffffffffffffffffffffff851681522054610182908363ffffffff6101ff16565b73ffffffffffffffffffffffffffffffffffffffff84166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350600192915050565b6000828211156101f957fe5b50900390565b8181018281101561020c57fe5b929150505600a165627a7a72305820ffbc864ae0e12556ab2bf80484305c35df7516452003ee5e545dd70b46e682720029 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/ERC20.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/ERC20Basic.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/ERC677.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/ERC677Receiver.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/ERC677Token.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/SafeMathChainlink.bin: -------------------------------------------------------------------------------- 1 | 0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820effc5047bf14d8bbc0dab09c9fbed3ddad2c63338802c025d11aebff071872c30029 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/bin/StandardToken.bin: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b50610735806100206000396000f30060806040526004361061008d5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461009257806318160ddd146100d757806323b872dd146100fe578063661884631461013557806370a0823114610166578063a9059cbb14610194578063d73dd623146101c5578063dd62ed3e146101f6575b600080fd5b34801561009e57600080fd5b506100c373ffffffffffffffffffffffffffffffffffffffff6004351660243561022a565b604080519115158252519081900360200190f35b3480156100e357600080fd5b506100ec61029d565b60408051918252519081900360200190f35b34801561010a57600080fd5b506100c373ffffffffffffffffffffffffffffffffffffffff600435811690602435166044356102a3565b34801561014157600080fd5b506100c373ffffffffffffffffffffffffffffffffffffffff600435166024356103e3565b34801561017257600080fd5b506100ec73ffffffffffffffffffffffffffffffffffffffff60043516610507565b3480156101a057600080fd5b506100c373ffffffffffffffffffffffffffffffffffffffff6004351660243561052f565b3480156101d157600080fd5b506100c373ffffffffffffffffffffffffffffffffffffffff600435166024356105f9565b34801561020257600080fd5b506100ec73ffffffffffffffffffffffffffffffffffffffff600435811690602435166106ac565b33600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60005481565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260026020908152604080832033845282528083205493835260019091528120549091906102f2908463ffffffff6106e416565b73ffffffffffffffffffffffffffffffffffffffff8087166000908152600160205260408082209390935590861681522054610334908463ffffffff6106f616565b73ffffffffffffffffffffffffffffffffffffffff851660009081526001602052604090205561036a818463ffffffff6106e416565b73ffffffffffffffffffffffffffffffffffffffff808716600081815260026020908152604080832033845282529182902094909455805187815290519288169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506001949350505050565b33600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152812054808311156104525733600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff88168452909152812055610494565b610462818463ffffffff6106e416565b33600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff891684529091529020555b33600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168085529083529281902054815190815290519293927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b3360009081526001602052604081205461054f908363ffffffff6106e416565b336000908152600160205260408082209290925573ffffffffffffffffffffffffffffffffffffffff85168152205461058e908363ffffffff6106f616565b73ffffffffffffffffffffffffffffffffffffffff84166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a350600192915050565b33600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915281205461063a908363ffffffff6106f616565b33600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168085529083529281902085905580519485525191937f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929081900390910190a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260026020908152604080832093909416825291909152205490565b6000828211156106f057fe5b50900390565b8181018281101561070357fe5b929150505600a165627a7a723058204e0a6fa1cf6d6408a03538d0507e12f0adebb51b070729fcf0bf4705e10009af0029 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/ERC677Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./interfaces/ERC677.sol"; 5 | import "./interfaces/ERC677Receiver.sol"; 6 | 7 | 8 | contract ERC677Token is ERC677 { 9 | 10 | /** 11 | * @dev transfer token to a contract address with additional data if the recipient is a contact. 12 | * @param _to The address to transfer to. 13 | * @param _value The amount to be transferred. 14 | * @param _data The extra data to be passed to the receiving contract. 15 | */ 16 | function transferAndCall(address _to, uint _value, bytes _data) 17 | public 18 | returns (bool success) 19 | { 20 | super.transfer(_to, _value); 21 | Transfer(msg.sender, _to, _value, _data); 22 | if (isContract(_to)) { 23 | contractFallback(_to, _value, _data); 24 | } 25 | return true; 26 | } 27 | 28 | 29 | // PRIVATE 30 | 31 | function contractFallback(address _to, uint _value, bytes _data) 32 | private 33 | { 34 | ERC677Receiver receiver = ERC677Receiver(_to); 35 | receiver.onTokenTransfer(msg.sender, _value, _data); 36 | } 37 | 38 | function isContract(address _addr) 39 | private 40 | returns (bool hasCode) 41 | { 42 | uint length; 43 | assembly { length := extcodesize(_addr) } 44 | return length > 0; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/LinkToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./ERC677Token.sol"; 5 | import { StandardToken as linkStandardToken } from "./vendor/StandardToken.sol"; 6 | 7 | 8 | contract LinkToken is linkStandardToken, ERC677Token { 9 | 10 | uint public constant totalSupply = 10**27; 11 | string public constant name = "ChainLink Token"; 12 | uint8 public constant decimals = 18; 13 | string public constant symbol = "LINK"; 14 | 15 | function LinkToken() 16 | public 17 | { 18 | balances[msg.sender] = totalSupply; 19 | } 20 | 21 | /** 22 | * @dev transfer token to a specified address with additional data if the recipient is a contract. 23 | * @param _to The address to transfer to. 24 | * @param _value The amount to be transferred. 25 | * @param _data The extra data to be passed to the receiving contract. 26 | */ 27 | function transferAndCall(address _to, uint _value, bytes _data) 28 | public 29 | validRecipient(_to) 30 | returns (bool success) 31 | { 32 | return super.transferAndCall(_to, _value, _data); 33 | } 34 | 35 | /** 36 | * @dev transfer token to a specified address. 37 | * @param _to The address to transfer to. 38 | * @param _value The amount to be transferred. 39 | */ 40 | function transfer(address _to, uint _value) 41 | public 42 | validRecipient(_to) 43 | returns (bool success) 44 | { 45 | return super.transfer(_to, _value); 46 | } 47 | 48 | /** 49 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 50 | * @param _spender The address which will spend the funds. 51 | * @param _value The amount of tokens to be spent. 52 | */ 53 | function approve(address _spender, uint256 _value) 54 | public 55 | validRecipient(_spender) 56 | returns (bool) 57 | { 58 | return super.approve(_spender, _value); 59 | } 60 | 61 | /** 62 | * @dev Transfer tokens from one address to another 63 | * @param _from address The address which you want to send tokens from 64 | * @param _to address The address which you want to transfer to 65 | * @param _value uint256 the amount of tokens to be transferred 66 | */ 67 | function transferFrom(address _from, address _to, uint256 _value) 68 | public 69 | validRecipient(_to) 70 | returns (bool) 71 | { 72 | return super.transferFrom(_from, _to, _value); 73 | } 74 | 75 | 76 | // MODIFIERS 77 | 78 | modifier validRecipient(address _recipient) { 79 | require(_recipient != address(0) && _recipient != address(this)); 80 | _; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/interfaces/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import { ERC20Basic as linkERC20Basic } from "./ERC20Basic.sol"; 5 | 6 | 7 | /** 8 | * @title ERC20 interface 9 | * @dev see https://github.com/ethereum/EIPs/issues/20 10 | */ 11 | contract ERC20 is linkERC20Basic { 12 | function allowance(address owner, address spender) constant returns (uint256); 13 | function transferFrom(address from, address to, uint256 value) returns (bool); 14 | function approve(address spender, uint256 value) returns (bool); 15 | event Approval(address indexed owner, address indexed spender, uint256 value); 16 | } 17 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/interfaces/ERC20Basic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | /** 5 | * @title ERC20Basic 6 | * @dev Simpler version of ERC20 interface 7 | * @dev see https://github.com/ethereum/EIPs/issues/179 8 | */ 9 | contract ERC20Basic { 10 | uint256 public totalSupply; 11 | function balanceOf(address who) constant returns (uint256); 12 | function transfer(address to, uint256 value) returns (bool); 13 | event Transfer(address indexed from, address indexed to, uint256 value); 14 | } 15 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/interfaces/ERC677.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import { ERC20 as linkERC20 } from "./ERC20.sol"; 4 | 5 | contract ERC677 is linkERC20 { 6 | function transferAndCall(address to, uint value, bytes data) returns (bool success); 7 | 8 | event Transfer(address indexed from, address indexed to, uint value, bytes data); 9 | } 10 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/interfaces/ERC677Receiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | contract ERC677Receiver { 5 | function onTokenTransfer(address _sender, uint _value, bytes _data); 6 | } 7 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/vendor/BasicToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | import { ERC20Basic as linkERC20Basic } from "../interfaces/ERC20Basic.sol"; 5 | import { SafeMathChainlink as linkSafeMath } from "./SafeMathChainlink.sol"; 6 | 7 | 8 | /** 9 | * @title Basic token 10 | * @dev Basic version of StandardToken, with no allowances. 11 | */ 12 | contract BasicToken is linkERC20Basic { 13 | using linkSafeMath for uint256; 14 | 15 | mapping(address => uint256) balances; 16 | 17 | /** 18 | * @dev transfer token for a specified address 19 | * @param _to The address to transfer to. 20 | * @param _value The amount to be transferred. 21 | */ 22 | function transfer(address _to, uint256 _value) returns (bool) { 23 | balances[msg.sender] = balances[msg.sender].sub(_value); 24 | balances[_to] = balances[_to].add(_value); 25 | Transfer(msg.sender, _to, _value); 26 | return true; 27 | } 28 | 29 | /** 30 | * @dev Gets the balance of the specified address. 31 | * @param _owner The address to query the the balance of. 32 | * @return An uint256 representing the amount owned by the passed address. 33 | */ 34 | function balanceOf(address _owner) constant returns (uint256 balance) { 35 | return balances[_owner]; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/vendor/SafeMathChainlink.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | /** 5 | * @title SafeMath 6 | * @dev Math operations with safety checks that throw on error 7 | */ 8 | library SafeMathChainlink { 9 | 10 | /** 11 | * @dev Multiplies two numbers, throws on overflow. 12 | */ 13 | function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { 14 | // Gas optimization: this is cheaper than asserting 'a' not being zero, but the 15 | // benefit is lost if 'b' is also tested. 16 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 17 | if (_a == 0) { 18 | return 0; 19 | } 20 | 21 | c = _a * _b; 22 | assert(c / _a == _b); 23 | return c; 24 | } 25 | 26 | /** 27 | * @dev Integer division of two numbers, truncating the quotient. 28 | */ 29 | function div(uint256 _a, uint256 _b) internal pure returns (uint256) { 30 | // assert(_b > 0); // Solidity automatically throws when dividing by 0 31 | // uint256 c = _a / _b; 32 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 33 | return _a / _b; 34 | } 35 | 36 | /** 37 | * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 38 | */ 39 | function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { 40 | assert(_b <= _a); 41 | return _a - _b; 42 | } 43 | 44 | /** 45 | * @dev Adds two numbers, throws on overflow. 46 | */ 47 | function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { 48 | c = _a + _b; 49 | assert(c >= _a); 50 | return c; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.4/src/vendor/StandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import { BasicToken as linkBasicToken } from "./BasicToken.sol"; 5 | import { ERC20 as linkERC20 } from "../interfaces/ERC20.sol"; 6 | 7 | 8 | /** 9 | * @title Standard ERC20 token 10 | * 11 | * @dev Implementation of the basic standard token. 12 | * @dev https://github.com/ethereum/EIPs/issues/20 13 | * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 14 | */ 15 | contract StandardToken is linkERC20, linkBasicToken { 16 | 17 | mapping (address => mapping (address => uint256)) allowed; 18 | 19 | 20 | /** 21 | * @dev Transfer tokens from one address to another 22 | * @param _from address The address which you want to send tokens from 23 | * @param _to address The address which you want to transfer to 24 | * @param _value uint256 the amount of tokens to be transferred 25 | */ 26 | function transferFrom(address _from, address _to, uint256 _value) returns (bool) { 27 | var _allowance = allowed[_from][msg.sender]; 28 | 29 | // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met 30 | // require (_value <= _allowance); 31 | 32 | balances[_from] = balances[_from].sub(_value); 33 | balances[_to] = balances[_to].add(_value); 34 | allowed[_from][msg.sender] = _allowance.sub(_value); 35 | Transfer(_from, _to, _value); 36 | return true; 37 | } 38 | 39 | /** 40 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 41 | * @param _spender The address which will spend the funds. 42 | * @param _value The amount of tokens to be spent. 43 | */ 44 | function approve(address _spender, uint256 _value) returns (bool) { 45 | allowed[msg.sender][_spender] = _value; 46 | Approval(msg.sender, _spender, _value); 47 | return true; 48 | } 49 | 50 | /** 51 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 52 | * @param _owner address The address which owns the funds. 53 | * @param _spender address The address which will spend the funds. 54 | * @return A uint256 specifying the amount of tokens still available for the spender. 55 | */ 56 | function allowance(address _owner, address _spender) constant returns (uint256 remaining) { 57 | return allowed[_owner][_spender]; 58 | } 59 | 60 | /* 61 | * approve should be called when allowed[_spender] == 0. To increment 62 | * allowed value is better to use this function to avoid 2 calls (and wait until 63 | * the first transaction is mined) 64 | * From MonolithDAO Token.sol 65 | */ 66 | function increaseApproval (address _spender, uint _addedValue) 67 | returns (bool success) { 68 | allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); 69 | Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 70 | return true; 71 | } 72 | 73 | function decreaseApproval (address _spender, uint _subtractedValue) 74 | returns (bool success) { 75 | uint oldValue = allowed[msg.sender][_spender]; 76 | if (_subtractedValue > oldValue) { 77 | allowed[msg.sender][_spender] = 0; 78 | } else { 79 | allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); 80 | } 81 | Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 82 | return true; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/AggregatorInterface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "int256", 8 | "name": "current", 9 | "type": "int256" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "uint256", 14 | "name": "roundId", 15 | "type": "uint256" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "updatedAt", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "AnswerUpdated", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "uint256", 33 | "name": "roundId", 34 | "type": "uint256" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "startedBy", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "startedAt", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "NewRound", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "uint256", 56 | "name": "roundId", 57 | "type": "uint256" 58 | } 59 | ], 60 | "name": "getAnswer", 61 | "outputs": [ 62 | { 63 | "internalType": "int256", 64 | "name": "", 65 | "type": "int256" 66 | } 67 | ], 68 | "stateMutability": "view", 69 | "type": "function" 70 | }, 71 | { 72 | "inputs": [ 73 | { 74 | "internalType": "uint256", 75 | "name": "roundId", 76 | "type": "uint256" 77 | } 78 | ], 79 | "name": "getTimestamp", 80 | "outputs": [ 81 | { 82 | "internalType": "uint256", 83 | "name": "", 84 | "type": "uint256" 85 | } 86 | ], 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [], 92 | "name": "latestAnswer", 93 | "outputs": [ 94 | { 95 | "internalType": "int256", 96 | "name": "", 97 | "type": "int256" 98 | } 99 | ], 100 | "stateMutability": "view", 101 | "type": "function" 102 | }, 103 | { 104 | "inputs": [], 105 | "name": "latestRound", 106 | "outputs": [ 107 | { 108 | "internalType": "uint256", 109 | "name": "", 110 | "type": "uint256" 111 | } 112 | ], 113 | "stateMutability": "view", 114 | "type": "function" 115 | }, 116 | { 117 | "inputs": [], 118 | "name": "latestTimestamp", 119 | "outputs": [ 120 | { 121 | "internalType": "uint256", 122 | "name": "", 123 | "type": "uint256" 124 | } 125 | ], 126 | "stateMutability": "view", 127 | "type": "function" 128 | } 129 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/AggregatorV3Interface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "decimals", 5 | "outputs": [ 6 | { 7 | "internalType": "uint8", 8 | "name": "", 9 | "type": "uint8" 10 | } 11 | ], 12 | "stateMutability": "view", 13 | "type": "function" 14 | }, 15 | { 16 | "inputs": [], 17 | "name": "description", 18 | "outputs": [ 19 | { 20 | "internalType": "string", 21 | "name": "", 22 | "type": "string" 23 | } 24 | ], 25 | "stateMutability": "view", 26 | "type": "function" 27 | }, 28 | { 29 | "inputs": [ 30 | { 31 | "internalType": "uint80", 32 | "name": "_roundId", 33 | "type": "uint80" 34 | } 35 | ], 36 | "name": "getRoundData", 37 | "outputs": [ 38 | { 39 | "internalType": "uint80", 40 | "name": "roundId", 41 | "type": "uint80" 42 | }, 43 | { 44 | "internalType": "int256", 45 | "name": "answer", 46 | "type": "int256" 47 | }, 48 | { 49 | "internalType": "uint256", 50 | "name": "startedAt", 51 | "type": "uint256" 52 | }, 53 | { 54 | "internalType": "uint256", 55 | "name": "updatedAt", 56 | "type": "uint256" 57 | }, 58 | { 59 | "internalType": "uint80", 60 | "name": "answeredInRound", 61 | "type": "uint80" 62 | } 63 | ], 64 | "stateMutability": "view", 65 | "type": "function" 66 | }, 67 | { 68 | "inputs": [], 69 | "name": "latestRoundData", 70 | "outputs": [ 71 | { 72 | "internalType": "uint80", 73 | "name": "roundId", 74 | "type": "uint80" 75 | }, 76 | { 77 | "internalType": "int256", 78 | "name": "answer", 79 | "type": "int256" 80 | }, 81 | { 82 | "internalType": "uint256", 83 | "name": "startedAt", 84 | "type": "uint256" 85 | }, 86 | { 87 | "internalType": "uint256", 88 | "name": "updatedAt", 89 | "type": "uint256" 90 | }, 91 | { 92 | "internalType": "uint80", 93 | "name": "answeredInRound", 94 | "type": "uint80" 95 | } 96 | ], 97 | "stateMutability": "view", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [], 102 | "name": "version", 103 | "outputs": [ 104 | { 105 | "internalType": "uint256", 106 | "name": "", 107 | "type": "uint256" 108 | } 109 | ], 110 | "stateMutability": "view", 111 | "type": "function" 112 | } 113 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/AggregatorValidatorInterface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256", 6 | "name": "previousRoundId", 7 | "type": "uint256" 8 | }, 9 | { 10 | "internalType": "int256", 11 | "name": "previousAnswer", 12 | "type": "int256" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "currentRoundId", 17 | "type": "uint256" 18 | }, 19 | { 20 | "internalType": "int256", 21 | "name": "currentAnswer", 22 | "type": "int256" 23 | } 24 | ], 25 | "name": "validate", 26 | "outputs": [ 27 | { 28 | "internalType": "bool", 29 | "name": "", 30 | "type": "bool" 31 | } 32 | ], 33 | "stateMutability": "nonpayable", 34 | "type": "function" 35 | } 36 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/Median.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/Owned.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "anonymous": false, 9 | "inputs": [ 10 | { 11 | "indexed": true, 12 | "internalType": "address", 13 | "name": "from", 14 | "type": "address" 15 | }, 16 | { 17 | "indexed": true, 18 | "internalType": "address", 19 | "name": "to", 20 | "type": "address" 21 | } 22 | ], 23 | "name": "OwnershipTransferRequested", 24 | "type": "event" 25 | }, 26 | { 27 | "anonymous": false, 28 | "inputs": [ 29 | { 30 | "indexed": true, 31 | "internalType": "address", 32 | "name": "from", 33 | "type": "address" 34 | }, 35 | { 36 | "indexed": true, 37 | "internalType": "address", 38 | "name": "to", 39 | "type": "address" 40 | } 41 | ], 42 | "name": "OwnershipTransferred", 43 | "type": "event" 44 | }, 45 | { 46 | "inputs": [], 47 | "name": "acceptOwnership", 48 | "outputs": [], 49 | "stateMutability": "nonpayable", 50 | "type": "function" 51 | }, 52 | { 53 | "inputs": [], 54 | "name": "owner", 55 | "outputs": [ 56 | { 57 | "internalType": "address", 58 | "name": "", 59 | "type": "address" 60 | } 61 | ], 62 | "stateMutability": "view", 63 | "type": "function" 64 | }, 65 | { 66 | "inputs": [ 67 | { 68 | "internalType": "address", 69 | "name": "_to", 70 | "type": "address" 71 | } 72 | ], 73 | "name": "transferOwnership", 74 | "outputs": [], 75 | "stateMutability": "nonpayable", 76 | "type": "function" 77 | } 78 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/SafeMath128.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/SafeMath32.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/SafeMath64.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/SafeMathChainlink.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/SignedSafeMath.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/abi/VRF.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "PROOF_LENGTH", 5 | "outputs": [ 6 | { 7 | "internalType": "uint256", 8 | "name": "", 9 | "type": "uint256" 10 | } 11 | ], 12 | "stateMutability": "view", 13 | "type": "function" 14 | } 15 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/AggregatorInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/AggregatorV2V3Interface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/AggregatorV3Interface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/AggregatorValidatorInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/LinkTokenInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/Median.bin: -------------------------------------------------------------------------------- 1 | 0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220a6f10300cd08eed797efb57778926e933eddbbdc3be078cc118b06254024acfc64736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/Owned.bin: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b50600080546001600160a01b0319163317905561025f806100326000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806379ba5097146100465780638da5cb5b14610050578063f2fde38b14610074575b600080fd5b61004e61009a565b005b61005861015d565b604080516001600160a01b039092168252519081900360200190f35b61004e6004803603602081101561008a57600080fd5b50356001600160a01b031661016c565b6001546001600160a01b031633146100f9576040805162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015290519081900360640190fd5b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000546001600160a01b031681565b6000546001600160a01b031633146101cb576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a35056fea26469706673582212206029bbb0ceb3bb82f6bf659dc9b06886945b25156e43fe75cb21d6532dacf5d364736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/SafeMath128.bin: -------------------------------------------------------------------------------- 1 | 0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212205e2e61ba7752040e974f17177d7193ae34c95c5621783f78c641fa8be574c15464736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/SafeMath32.bin: -------------------------------------------------------------------------------- 1 | 0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220f039807225d5b9998b45163d9715586a296d6714385abb47baf652d58ae29f4964736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/SafeMath64.bin: -------------------------------------------------------------------------------- 1 | 0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220ce8dd543f5b6829c6270bcf31dbb460b4107188dbaac939f9378d04e953623ad64736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/SafeMathChainlink.bin: -------------------------------------------------------------------------------- 1 | 0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212207e71e9acfe8602d980e5604ebc7a7c0ec2d823a70e9ac7b4d33b3dd23702268864736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/SignedSafeMath.bin: -------------------------------------------------------------------------------- 1 | 0x60566023600b82828239805160001a607314601657fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122012e839352a051881dc460eccb92486f365b4766ef185792c4e0dd6dc3045ee8a64736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/bin/VRF.bin: -------------------------------------------------------------------------------- 1 | 0x6080604052348015600f57600080fd5b5060818061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e911439c14602d575b600080fd5b60336045565b60408051918252519081900360200190f35b6101a08156fea2646970667358221220bdee0952050f8dedef8ba3b59b8b57f72354c75939413fb4ad391930db3228d764736f6c63430006060033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/Owned.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | /** 5 | * @title The Owned contract 6 | * @notice A contract with helpers for basic contract ownership. 7 | */ 8 | contract Owned { 9 | 10 | address public owner; 11 | address private pendingOwner; 12 | 13 | event OwnershipTransferRequested( 14 | address indexed from, 15 | address indexed to 16 | ); 17 | event OwnershipTransferred( 18 | address indexed from, 19 | address indexed to 20 | ); 21 | 22 | constructor() public { 23 | owner = msg.sender; 24 | } 25 | 26 | /** 27 | * @dev Allows an owner to begin transferring ownership to a new address, 28 | * pending. 29 | */ 30 | function transferOwnership(address _to) 31 | external 32 | onlyOwner() 33 | { 34 | pendingOwner = _to; 35 | 36 | emit OwnershipTransferRequested(owner, _to); 37 | } 38 | 39 | /** 40 | * @dev Allows an ownership transfer to be completed by the recipient. 41 | */ 42 | function acceptOwnership() 43 | external 44 | { 45 | require(msg.sender == pendingOwner, "Must be proposed owner"); 46 | 47 | address oldOwner = owner; 48 | owner = msg.sender; 49 | pendingOwner = address(0); 50 | 51 | emit OwnershipTransferred(oldOwner, msg.sender); 52 | } 53 | 54 | /** 55 | * @dev Reverts if called by anyone other than the contract owner. 56 | */ 57 | modifier onlyOwner() { 58 | require(msg.sender == owner, "Only callable by owner"); 59 | _; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/SafeMath32.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | /** 5 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 6 | * checks. 7 | * 8 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 9 | * in bugs, because programmers usually assume that an overflow raises an 10 | * error, which is the standard behavior in high level programming languages. 11 | * `SafeMath` restores this intuition by reverting the transaction when an 12 | * operation overflows. 13 | * 14 | * Using this library instead of the unchecked operations eliminates an entire 15 | * class of bugs, so it's recommended to use it always. 16 | * 17 | * This library is a version of Open Zeppelin's SafeMath, modified to support 18 | * unsigned 32 bit integers. 19 | */ 20 | library SafeMath32 { 21 | /** 22 | * @dev Returns the addition of two unsigned integers, reverting on 23 | * overflow. 24 | * 25 | * Counterpart to Solidity's `+` operator. 26 | * 27 | * Requirements: 28 | * - Addition cannot overflow. 29 | */ 30 | function add(uint32 a, uint32 b) internal pure returns (uint32) { 31 | uint32 c = a + b; 32 | require(c >= a, "SafeMath: addition overflow"); 33 | 34 | return c; 35 | } 36 | 37 | /** 38 | * @dev Returns the subtraction of two unsigned integers, reverting on 39 | * overflow (when the result is negative). 40 | * 41 | * Counterpart to Solidity's `-` operator. 42 | * 43 | * Requirements: 44 | * - Subtraction cannot overflow. 45 | */ 46 | function sub(uint32 a, uint32 b) internal pure returns (uint32) { 47 | require(b <= a, "SafeMath: subtraction overflow"); 48 | uint32 c = a - b; 49 | 50 | return c; 51 | } 52 | 53 | /** 54 | * @dev Returns the multiplication of two unsigned integers, reverting on 55 | * overflow. 56 | * 57 | * Counterpart to Solidity's `*` operator. 58 | * 59 | * Requirements: 60 | * - Multiplication cannot overflow. 61 | */ 62 | function mul(uint32 a, uint32 b) internal pure returns (uint32) { 63 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 64 | // benefit is lost if 'b' is also tested. 65 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 66 | if (a == 0) { 67 | return 0; 68 | } 69 | 70 | uint32 c = a * b; 71 | require(c / a == b, "SafeMath: multiplication overflow"); 72 | 73 | return c; 74 | } 75 | 76 | /** 77 | * @dev Returns the integer division of two unsigned integers. Reverts on 78 | * division by zero. The result is rounded towards zero. 79 | * 80 | * Counterpart to Solidity's `/` operator. Note: this function uses a 81 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 82 | * uses an invalid opcode to revert (consuming all remaining gas). 83 | * 84 | * Requirements: 85 | * - The divisor cannot be zero. 86 | */ 87 | function div(uint32 a, uint32 b) internal pure returns (uint32) { 88 | // Solidity only automatically asserts when dividing by 0 89 | require(b > 0, "SafeMath: division by zero"); 90 | uint32 c = a / b; 91 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 92 | 93 | return c; 94 | } 95 | 96 | /** 97 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 98 | * Reverts when dividing by zero. 99 | * 100 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 101 | * opcode (which leaves remaining gas untouched) while Solidity uses an 102 | * invalid opcode to revert (consuming all remaining gas). 103 | * 104 | * Requirements: 105 | * - The divisor cannot be zero. 106 | */ 107 | function mod(uint32 a, uint32 b) internal pure returns (uint32) { 108 | require(b != 0, "SafeMath: modulo by zero"); 109 | return a % b; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/SignedSafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | library SignedSafeMath { 5 | int256 constant private _INT256_MIN = -2**255; 6 | 7 | /** 8 | * @dev Multiplies two signed integers, reverts on overflow. 9 | */ 10 | function mul(int256 a, int256 b) internal pure returns (int256) { 11 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 12 | // benefit is lost if 'b' is also tested. 13 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 14 | if (a == 0) { 15 | return 0; 16 | } 17 | 18 | require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); 19 | 20 | int256 c = a * b; 21 | require(c / a == b, "SignedSafeMath: multiplication overflow"); 22 | 23 | return c; 24 | } 25 | 26 | /** 27 | * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero. 28 | */ 29 | function div(int256 a, int256 b) internal pure returns (int256) { 30 | require(b != 0, "SignedSafeMath: division by zero"); 31 | require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); 32 | 33 | int256 c = a / b; 34 | 35 | return c; 36 | } 37 | 38 | /** 39 | * @dev Subtracts two signed integers, reverts on overflow. 40 | */ 41 | function sub(int256 a, int256 b) internal pure returns (int256) { 42 | int256 c = a - b; 43 | require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); 44 | 45 | return c; 46 | } 47 | 48 | /** 49 | * @dev Adds two signed integers, reverts on overflow. 50 | */ 51 | function add(int256 a, int256 b) internal pure returns (int256) { 52 | int256 c = a + b; 53 | require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); 54 | 55 | return c; 56 | } 57 | 58 | /** 59 | * @notice Computes average of two signed integers, ensuring that the computation 60 | * doesn't overflow. 61 | * @dev If the result is not an integer, it is rounded towards zero. For example, 62 | * avg(-3, -4) = -3 63 | */ 64 | function avg(int256 _a, int256 _b) 65 | internal 66 | pure 67 | returns (int256) 68 | { 69 | if ((_a < 0 && _b > 0) || (_a > 0 && _b < 0)) { 70 | return add(_a, _b) / 2; 71 | } 72 | int256 remainder = (_a % 2 + _b % 2) / 2; 73 | return add(add(_a / 2, _b / 2), remainder); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/interfaces/AggregatorInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0; 3 | 4 | interface AggregatorInterface { 5 | function latestAnswer() external view returns (int256); 6 | function latestTimestamp() external view returns (uint256); 7 | function latestRound() external view returns (uint256); 8 | function getAnswer(uint256 roundId) external view returns (int256); 9 | function getTimestamp(uint256 roundId) external view returns (uint256); 10 | 11 | event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); 12 | event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); 13 | } 14 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/interfaces/AggregatorV2V3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0; 3 | 4 | import "./AggregatorInterface.sol"; 5 | import "./AggregatorV3Interface.sol"; 6 | 7 | interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface 8 | { 9 | } 10 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/interfaces/AggregatorV3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0; 3 | 4 | interface AggregatorV3Interface { 5 | 6 | function decimals() external view returns (uint8); 7 | function description() external view returns (string memory); 8 | function version() external view returns (uint256); 9 | 10 | // getRoundData and latestRoundData should both raise "No data present" 11 | // if they do not have data to report, instead of returning unset values 12 | // which could be misinterpreted as actual reported values. 13 | function getRoundData(uint80 _roundId) 14 | external 15 | view 16 | returns ( 17 | uint80 roundId, 18 | int256 answer, 19 | uint256 startedAt, 20 | uint256 updatedAt, 21 | uint80 answeredInRound 22 | ); 23 | function latestRoundData() 24 | external 25 | view 26 | returns ( 27 | uint80 roundId, 28 | int256 answer, 29 | uint256 startedAt, 30 | uint256 updatedAt, 31 | uint80 answeredInRound 32 | ); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/interfaces/AggregatorValidatorInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | interface AggregatorValidatorInterface { 5 | function validate( 6 | uint256 previousRoundId, 7 | int256 previousAnswer, 8 | uint256 currentRoundId, 9 | int256 currentAnswer 10 | ) external returns (bool); 11 | } 12 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/interfaces/LinkTokenInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | interface LinkTokenInterface { 5 | function allowance(address owner, address spender) external view returns (uint256 remaining); 6 | function approve(address spender, uint256 value) external returns (bool success); 7 | function balanceOf(address owner) external view returns (uint256 balance); 8 | function decimals() external view returns (uint8 decimalPlaces); 9 | function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); 10 | function increaseApproval(address spender, uint256 subtractedValue) external; 11 | function name() external view returns (string memory tokenName); 12 | function symbol() external view returns (string memory tokenSymbol); 13 | function totalSupply() external view returns (uint256 totalTokensIssued); 14 | function transfer(address to, uint256 value) external returns (bool success); 15 | function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); 16 | function transferFrom(address from, address to, uint256 value) external returns (bool success); 17 | } 18 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.6/src/vendor/SafeMathChainlink.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | /** 5 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 6 | * checks. 7 | * 8 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 9 | * in bugs, because programmers usually assume that an overflow raises an 10 | * error, which is the standard behavior in high level programming languages. 11 | * `SafeMath` restores this intuition by reverting the transaction when an 12 | * operation overflows. 13 | * 14 | * Using this library instead of the unchecked operations eliminates an entire 15 | * class of bugs, so it's recommended to use it always. 16 | */ 17 | library SafeMathChainlink { 18 | /** 19 | * @dev Returns the addition of two unsigned integers, reverting on 20 | * overflow. 21 | * 22 | * Counterpart to Solidity's `+` operator. 23 | * 24 | * Requirements: 25 | * - Addition cannot overflow. 26 | */ 27 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 28 | uint256 c = a + b; 29 | require(c >= a, "SafeMath: addition overflow"); 30 | 31 | return c; 32 | } 33 | 34 | /** 35 | * @dev Returns the subtraction of two unsigned integers, reverting on 36 | * overflow (when the result is negative). 37 | * 38 | * Counterpart to Solidity's `-` operator. 39 | * 40 | * Requirements: 41 | * - Subtraction cannot overflow. 42 | */ 43 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 44 | require(b <= a, "SafeMath: subtraction overflow"); 45 | uint256 c = a - b; 46 | 47 | return c; 48 | } 49 | 50 | /** 51 | * @dev Returns the multiplication of two unsigned integers, reverting on 52 | * overflow. 53 | * 54 | * Counterpart to Solidity's `*` operator. 55 | * 56 | * Requirements: 57 | * - Multiplication cannot overflow. 58 | */ 59 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 60 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 61 | // benefit is lost if 'b' is also tested. 62 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 63 | if (a == 0) { 64 | return 0; 65 | } 66 | 67 | uint256 c = a * b; 68 | require(c / a == b, "SafeMath: multiplication overflow"); 69 | 70 | return c; 71 | } 72 | 73 | /** 74 | * @dev Returns the integer division of two unsigned integers. Reverts on 75 | * division by zero. The result is rounded towards zero. 76 | * 77 | * Counterpart to Solidity's `/` operator. Note: this function uses a 78 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 79 | * uses an invalid opcode to revert (consuming all remaining gas). 80 | * 81 | * Requirements: 82 | * - The divisor cannot be zero. 83 | */ 84 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 85 | // Solidity only automatically asserts when dividing by 0 86 | require(b > 0, "SafeMath: division by zero"); 87 | uint256 c = a / b; 88 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 89 | 90 | return c; 91 | } 92 | 93 | /** 94 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 95 | * Reverts when dividing by zero. 96 | * 97 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 98 | * opcode (which leaves remaining gas untouched) while Solidity uses an 99 | * invalid opcode to revert (consuming all remaining gas). 100 | * 101 | * Requirements: 102 | * - The divisor cannot be zero. 103 | */ 104 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 105 | require(b != 0, "SafeMath: modulo by zero"); 106 | return a % b; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/abi/AccessControllerInterface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "user", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "bytes", 11 | "name": "data", 12 | "type": "bytes" 13 | } 14 | ], 15 | "name": "hasAccess", 16 | "outputs": [ 17 | { 18 | "internalType": "bool", 19 | "name": "", 20 | "type": "bool" 21 | } 22 | ], 23 | "stateMutability": "view", 24 | "type": "function" 25 | } 26 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/abi/AggregatorInterface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "int256", 8 | "name": "current", 9 | "type": "int256" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "uint256", 14 | "name": "roundId", 15 | "type": "uint256" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "updatedAt", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "AnswerUpdated", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "uint256", 33 | "name": "roundId", 34 | "type": "uint256" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "startedBy", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "startedAt", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "NewRound", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "uint256", 56 | "name": "roundId", 57 | "type": "uint256" 58 | } 59 | ], 60 | "name": "getAnswer", 61 | "outputs": [ 62 | { 63 | "internalType": "int256", 64 | "name": "", 65 | "type": "int256" 66 | } 67 | ], 68 | "stateMutability": "view", 69 | "type": "function" 70 | }, 71 | { 72 | "inputs": [ 73 | { 74 | "internalType": "uint256", 75 | "name": "roundId", 76 | "type": "uint256" 77 | } 78 | ], 79 | "name": "getTimestamp", 80 | "outputs": [ 81 | { 82 | "internalType": "uint256", 83 | "name": "", 84 | "type": "uint256" 85 | } 86 | ], 87 | "stateMutability": "view", 88 | "type": "function" 89 | }, 90 | { 91 | "inputs": [], 92 | "name": "latestAnswer", 93 | "outputs": [ 94 | { 95 | "internalType": "int256", 96 | "name": "", 97 | "type": "int256" 98 | } 99 | ], 100 | "stateMutability": "view", 101 | "type": "function" 102 | }, 103 | { 104 | "inputs": [], 105 | "name": "latestRound", 106 | "outputs": [ 107 | { 108 | "internalType": "uint256", 109 | "name": "", 110 | "type": "uint256" 111 | } 112 | ], 113 | "stateMutability": "view", 114 | "type": "function" 115 | }, 116 | { 117 | "inputs": [], 118 | "name": "latestTimestamp", 119 | "outputs": [ 120 | { 121 | "internalType": "uint256", 122 | "name": "", 123 | "type": "uint256" 124 | } 125 | ], 126 | "stateMutability": "view", 127 | "type": "function" 128 | } 129 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/abi/AggregatorV3Interface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "decimals", 5 | "outputs": [ 6 | { 7 | "internalType": "uint8", 8 | "name": "", 9 | "type": "uint8" 10 | } 11 | ], 12 | "stateMutability": "view", 13 | "type": "function" 14 | }, 15 | { 16 | "inputs": [], 17 | "name": "description", 18 | "outputs": [ 19 | { 20 | "internalType": "string", 21 | "name": "", 22 | "type": "string" 23 | } 24 | ], 25 | "stateMutability": "view", 26 | "type": "function" 27 | }, 28 | { 29 | "inputs": [ 30 | { 31 | "internalType": "uint80", 32 | "name": "_roundId", 33 | "type": "uint80" 34 | } 35 | ], 36 | "name": "getRoundData", 37 | "outputs": [ 38 | { 39 | "internalType": "uint80", 40 | "name": "roundId", 41 | "type": "uint80" 42 | }, 43 | { 44 | "internalType": "int256", 45 | "name": "answer", 46 | "type": "int256" 47 | }, 48 | { 49 | "internalType": "uint256", 50 | "name": "startedAt", 51 | "type": "uint256" 52 | }, 53 | { 54 | "internalType": "uint256", 55 | "name": "updatedAt", 56 | "type": "uint256" 57 | }, 58 | { 59 | "internalType": "uint80", 60 | "name": "answeredInRound", 61 | "type": "uint80" 62 | } 63 | ], 64 | "stateMutability": "view", 65 | "type": "function" 66 | }, 67 | { 68 | "inputs": [], 69 | "name": "latestRoundData", 70 | "outputs": [ 71 | { 72 | "internalType": "uint80", 73 | "name": "roundId", 74 | "type": "uint80" 75 | }, 76 | { 77 | "internalType": "int256", 78 | "name": "answer", 79 | "type": "int256" 80 | }, 81 | { 82 | "internalType": "uint256", 83 | "name": "startedAt", 84 | "type": "uint256" 85 | }, 86 | { 87 | "internalType": "uint256", 88 | "name": "updatedAt", 89 | "type": "uint256" 90 | }, 91 | { 92 | "internalType": "uint80", 93 | "name": "answeredInRound", 94 | "type": "uint80" 95 | } 96 | ], 97 | "stateMutability": "view", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [], 102 | "name": "version", 103 | "outputs": [ 104 | { 105 | "internalType": "uint256", 106 | "name": "", 107 | "type": "uint256" 108 | } 109 | ], 110 | "stateMutability": "view", 111 | "type": "function" 112 | } 113 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/abi/AggregatorValidatorInterface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256", 6 | "name": "previousRoundId", 7 | "type": "uint256" 8 | }, 9 | { 10 | "internalType": "int256", 11 | "name": "previousAnswer", 12 | "type": "int256" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "currentRoundId", 17 | "type": "uint256" 18 | }, 19 | { 20 | "internalType": "int256", 21 | "name": "currentAnswer", 22 | "type": "int256" 23 | } 24 | ], 25 | "name": "validate", 26 | "outputs": [ 27 | { 28 | "internalType": "bool", 29 | "name": "", 30 | "type": "bool" 31 | } 32 | ], 33 | "stateMutability": "nonpayable", 34 | "type": "function" 35 | } 36 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/abi/Owned.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "anonymous": false, 9 | "inputs": [ 10 | { 11 | "indexed": true, 12 | "internalType": "address", 13 | "name": "from", 14 | "type": "address" 15 | }, 16 | { 17 | "indexed": true, 18 | "internalType": "address", 19 | "name": "to", 20 | "type": "address" 21 | } 22 | ], 23 | "name": "OwnershipTransferRequested", 24 | "type": "event" 25 | }, 26 | { 27 | "anonymous": false, 28 | "inputs": [ 29 | { 30 | "indexed": true, 31 | "internalType": "address", 32 | "name": "from", 33 | "type": "address" 34 | }, 35 | { 36 | "indexed": true, 37 | "internalType": "address", 38 | "name": "to", 39 | "type": "address" 40 | } 41 | ], 42 | "name": "OwnershipTransferred", 43 | "type": "event" 44 | }, 45 | { 46 | "inputs": [], 47 | "name": "acceptOwnership", 48 | "outputs": [], 49 | "stateMutability": "nonpayable", 50 | "type": "function" 51 | }, 52 | { 53 | "inputs": [], 54 | "name": "owner", 55 | "outputs": [ 56 | { 57 | "internalType": "address payable", 58 | "name": "", 59 | "type": "address" 60 | } 61 | ], 62 | "stateMutability": "view", 63 | "type": "function" 64 | }, 65 | { 66 | "inputs": [ 67 | { 68 | "internalType": "address", 69 | "name": "_to", 70 | "type": "address" 71 | } 72 | ], 73 | "name": "transferOwnership", 74 | "outputs": [], 75 | "stateMutability": "nonpayable", 76 | "type": "function" 77 | } 78 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/abi/TypeAndVersionInterface.abi: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "typeAndVersion", 5 | "outputs": [ 6 | { 7 | "internalType": "string", 8 | "name": "", 9 | "type": "string" 10 | } 11 | ], 12 | "stateMutability": "pure", 13 | "type": "function" 14 | } 15 | ] -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/AccessControllerInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/AggregatorInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/AggregatorV2V3Interface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/AggregatorV3Interface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/AggregatorValidatorInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/LinkTokenInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/Owned.bin: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b50600080546001600160a01b0319163317905561025f806100326000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806379ba5097146100465780638da5cb5b14610050578063f2fde38b14610074575b600080fd5b61004e61009a565b005b61005861015d565b604080516001600160a01b039092168252519081900360200190f35b61004e6004803603602081101561008a57600080fd5b50356001600160a01b031661016c565b6001546001600160a01b031633146100f9576040805162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015290519081900360640190fd5b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000546001600160a01b031681565b6000546001600160a01b031633146101cb576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a35056fea2646970667358221220770b8e8fcb666db061119ffacda3872f13a76aebcc8221fce660eb4c69f94f0364736f6c63430007010033 -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/bin/TypeAndVersionInterface.bin: -------------------------------------------------------------------------------- 1 | 0x -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/AccessControllerInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface AccessControllerInterface { 5 | function hasAccess(address user, bytes calldata data) external view returns (bool); 6 | } 7 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/AggregatorInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface AggregatorInterface { 5 | function latestAnswer() external view returns (int256); 6 | function latestTimestamp() external view returns (uint256); 7 | function latestRound() external view returns (uint256); 8 | function getAnswer(uint256 roundId) external view returns (int256); 9 | function getTimestamp(uint256 roundId) external view returns (uint256); 10 | 11 | event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); 12 | event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); 13 | } 14 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/AggregatorV2V3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | import "./AggregatorInterface.sol"; 5 | import "./AggregatorV3Interface.sol"; 6 | 7 | interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface 8 | { 9 | } -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/AggregatorV3Interface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface AggregatorV3Interface { 5 | 6 | function decimals() external view returns (uint8); 7 | function description() external view returns (string memory); 8 | function version() external view returns (uint256); 9 | 10 | // getRoundData and latestRoundData should both raise "No data present" 11 | // if they do not have data to report, instead of returning unset values 12 | // which could be misinterpreted as actual reported values. 13 | function getRoundData(uint80 _roundId) 14 | external 15 | view 16 | returns ( 17 | uint80 roundId, 18 | int256 answer, 19 | uint256 startedAt, 20 | uint256 updatedAt, 21 | uint80 answeredInRound 22 | ); 23 | function latestRoundData() 24 | external 25 | view 26 | returns ( 27 | uint80 roundId, 28 | int256 answer, 29 | uint256 startedAt, 30 | uint256 updatedAt, 31 | uint80 answeredInRound 32 | ); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/AggregatorValidatorInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | interface AggregatorValidatorInterface { 5 | function validate( 6 | uint256 previousRoundId, 7 | int256 previousAnswer, 8 | uint256 currentRoundId, 9 | int256 currentAnswer 10 | ) external returns (bool); 11 | } -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/LinkTokenInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.1; 3 | 4 | interface LinkTokenInterface { 5 | function allowance(address owner, address spender) external view returns (uint256 remaining); 6 | function approve(address spender, uint256 value) external returns (bool success); 7 | function balanceOf(address owner) external view returns (uint256 balance); 8 | function decimals() external view returns (uint8 decimalPlaces); 9 | function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); 10 | function increaseApproval(address spender, uint256 subtractedValue) external; 11 | function name() external view returns (string memory tokenName); 12 | function symbol() external view returns (string memory tokenSymbol); 13 | function totalSupply() external view returns (uint256 totalTokensIssued); 14 | function transfer(address to, uint256 value) external returns (bool success); 15 | function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); 16 | function transferFrom(address from, address to, uint256 value) external returns (bool success); 17 | } 18 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/Owned.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | /** 5 | * @title The Owned contract 6 | * @notice A contract with helpers for basic contract ownership. 7 | */ 8 | contract Owned { 9 | 10 | address payable public owner; 11 | address private pendingOwner; 12 | 13 | event OwnershipTransferRequested( 14 | address indexed from, 15 | address indexed to 16 | ); 17 | event OwnershipTransferred( 18 | address indexed from, 19 | address indexed to 20 | ); 21 | 22 | constructor() { 23 | owner = msg.sender; 24 | } 25 | 26 | /** 27 | * @dev Allows an owner to begin transferring ownership to a new address, 28 | * pending. 29 | */ 30 | function transferOwnership(address _to) 31 | external 32 | onlyOwner() 33 | { 34 | pendingOwner = _to; 35 | 36 | emit OwnershipTransferRequested(owner, _to); 37 | } 38 | 39 | /** 40 | * @dev Allows an ownership transfer to be completed by the recipient. 41 | */ 42 | function acceptOwnership() 43 | external 44 | { 45 | require(msg.sender == pendingOwner, "Must be proposed owner"); 46 | 47 | address oldOwner = owner; 48 | owner = msg.sender; 49 | pendingOwner = address(0); 50 | 51 | emit OwnershipTransferred(oldOwner, msg.sender); 52 | } 53 | 54 | /** 55 | * @dev Reverts if called by anyone other than the contract owner. 56 | */ 57 | modifier onlyOwner() { 58 | require(msg.sender == owner, "Only callable by owner"); 59 | _; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/contracts/ethereum/v0.7/src/TypeAndVersionInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.0; 3 | 4 | abstract contract TypeAndVersionInterface{ 5 | function typeAndVersion() 6 | external 7 | pure 8 | virtual 9 | returns (string memory); 10 | } -------------------------------------------------------------------------------- /fm-demo/go-scripts/go.mod: -------------------------------------------------------------------------------- 1 | module integrations-framework 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/ethereum/go-ethereum v1.10.1 7 | github.com/google/uuid v1.2.0 // indirect 8 | github.com/onsi/ginkgo v1.14.0 9 | github.com/onsi/gomega v1.10.1 10 | github.com/rs/zerolog v1.21.0 11 | github.com/spf13/viper v1.7.1 12 | github.com/stretchr/testify v1.7.0 13 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "integrations-framework/client" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "os" 10 | ) 11 | 12 | func main() { 13 | files, err := ioutil.ReadDir("../specs") 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | for _, file := range files { 19 | jsonFile, err := os.Open("../specs/" + file.Name()) 20 | if err != nil { 21 | fmt.Println(err) 22 | } 23 | defer jsonFile.Close() 24 | 25 | spec, _ := ioutil.ReadAll(jsonFile) 26 | c := newDefaultClient("http://localhost:6688") 27 | c.SetClient(&http.Client{}) 28 | c.SetSessionCookie() 29 | s, err := c.CreateSpec(string(spec)) 30 | if err != nil { 31 | fmt.Println(err) 32 | } 33 | fmt.Println("Job spec created", s) 34 | } 35 | } 36 | 37 | func newDefaultClient(url string) client.Chainlink { 38 | cl := client.NewChainlink(&client.ChainlinkConfig{ 39 | Email: "notreal@fakeemail.ch", 40 | Password: "twochains", 41 | URL: url, 42 | }) 43 | return cl 44 | } 45 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/specs/spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "initiators": [ 3 | { 4 | "type": "external", 5 | "params": { 6 | "name": "substrate", 7 | "body": { 8 | "endpoint": "substrate", 9 | "feed_id": 0, 10 | "account_id": "0x7c522c8273973e7bcf4a5dbfcc745dba4a3ab08c1e410167d7b1bdf9cb924f6c", 11 | "fluxmonitor": { 12 | "requestData": { 13 | "data": { "from": "DOT", "to": "USD" } 14 | }, 15 | "feeds": [{ "url": "http://localhost:8083" }], 16 | "threshold": 0.5, 17 | "absoluteThreshold": 0, 18 | "precision": 8, 19 | "pollTimer": { "period": "3s" }, 20 | "idleTimer": { "duration": "10s" } 21 | } 22 | } 23 | } 24 | } 25 | ], 26 | "tasks": [ 27 | { 28 | "type": "ethtx", 29 | "params": { "multiply": 1e8 } 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /fm-demo/go-scripts/tools/README.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | Here are some QoL tools used by the framework. 4 | 5 | ## `tools.go` 6 | 7 | Basic import for the [ginkgo test framework](https://github.com/onsi/ginkgo) 8 | 9 | ## `compile_contracts.py` 10 | 11 | A proof of concept script to conveniently compile solidity source and generate golang bindings. 12 | 13 | Run with `python3 ./tools/compile_contracts.py` 14 | 15 | This will: 16 | 17 | 1. Install a local version of `hardhat` 18 | 2. Use `hardhat` to compile solidity source code 19 | 3. Use `abigen` to generate golang bindings for the compiled contracts 20 | 4. Cleanup `hardhat` installation and files 21 | 22 | -------------------------------------------------------------------------------- /fm-demo/go-scripts/tools/compile_contracts.py: -------------------------------------------------------------------------------- 1 | import json 2 | import subprocess 3 | import os 4 | from os import path 5 | import shutil 6 | 7 | # A proof of concept / convenient script to quickly compile contracts and their go bindings 8 | 9 | solc_versions = ["v0.4", "v0.6", "v0.7"] 10 | rootdir = "./artifacts/contracts/ethereum/" 11 | targetdir = "./contracts/ethereum" 12 | 13 | used_contract_names = ["FluxAggregator", "VRF", "OffchainAggregator", "LinkToken"] 14 | 15 | print("Locally installing hardhat...") 16 | subprocess.run('npm install --save-dev hardhat', shell=True, check=True) 17 | 18 | print("Modifying hardhat settings...") 19 | with open("hardhat.config.js", "w") as hardhat_config: 20 | hardhat_config.write("""module.exports = { 21 | solidity: { 22 | compilers: [ 23 | { 24 | version: "0.8.0", 25 | settings: { 26 | optimizer: { 27 | enabled: true, 28 | runs: 1000 29 | } 30 | } 31 | }, 32 | { 33 | version: "0.7.1", 34 | settings: { 35 | optimizer: { 36 | enabled: true, 37 | runs: 1000 38 | } 39 | } 40 | }, 41 | { 42 | version: "0.7.0", 43 | settings: { 44 | optimizer: { 45 | enabled: true, 46 | runs: 1000 47 | } 48 | } 49 | }, 50 | { 51 | version: "0.6.6", 52 | settings: { 53 | optimizer: { 54 | enabled: true, 55 | runs: 1000 56 | } 57 | } 58 | }, 59 | { 60 | version: "0.4.11", 61 | settings: { 62 | optimizer: { 63 | enabled: true, 64 | runs: 1000 65 | } 66 | } 67 | }, 68 | { 69 | version: "0.4.24", 70 | settings: { 71 | optimizer: { 72 | enabled: true, 73 | runs: 1000 74 | } 75 | } 76 | } 77 | ] 78 | } 79 | }""") 80 | 81 | print("Compiling contracts...") 82 | subprocess.run('npx hardhat compile', shell=True, check=True) 83 | 84 | print("Creating contract go bindings...") 85 | for version in solc_versions: 86 | for subdir, dirs, files in os.walk(rootdir + version): 87 | for f in files: 88 | if ".dbg." not in f: 89 | print(f) 90 | compile_contract = open(subdir + "/" + f, "r") 91 | data = json.load(compile_contract) 92 | contract_name = data["contractName"] 93 | 94 | abi_name = targetdir + "/" + version + "/abi/" + contract_name + ".abi" 95 | abi_file = open(abi_name, "w") 96 | abi_file.write(json.dumps(data["abi"], indent=2)) 97 | 98 | bin_name = targetdir + "/" + version + "/bin/" + contract_name + ".bin" 99 | bin_file = open(bin_name, "w") 100 | bin_file.write(str(data["bytecode"])) 101 | abi_file.close() 102 | bin_file.close() 103 | 104 | if contract_name in used_contract_names: 105 | subprocess.run("abigen --bin=" + bin_name + " --abi=" + abi_name + " --pkg=" + contract_name + " --out=" + 106 | targetdir + "/" + contract_name + ".go", shell=True, check=True) 107 | 108 | print("Cleaning up Hardhat...") 109 | subprocess.run('npm uninstall --save-dev hardhat', shell=True) 110 | if path.exists("hardhat.config.js"): 111 | os.remove("hardhat.config.js") 112 | if path.exists("package-lock.json"): 113 | os.remove("package-lock.json") 114 | if path.exists("package.json"): 115 | os.remove("package.json") 116 | if path.exists("node_modules/"): 117 | shutil.rmtree("node_modules/") 118 | if path.exists("artifacts/"): 119 | shutil.rmtree("artifacts/") 120 | if path.exists("cache/"): 121 | shutil.rmtree("cache/") 122 | 123 | print("Done!") -------------------------------------------------------------------------------- /fm-demo/go-scripts/tools/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/onsi/ginkgo/ginkgo" 7 | ) 8 | 9 | // This file imports packages that are used when running go generate, or used 10 | // during the development process but not otherwise depended on by built code. 11 | -------------------------------------------------------------------------------- /fm-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fm-demo", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "@polkadot/api": "^4.10.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /fm-demo/scripts/add-bridges.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./scripts/common.sh 4 | 5 | echo "Adding Bridges to Chainlink node..." 6 | 7 | CL_URL="http://localhost:6688" 8 | 9 | login_cl "$CL_URL" 10 | 11 | payload=$( 12 | cat </dev/null 30 | curl -s -b ./cookiefile -d "$payload2" -X POST -H 'Content-Type: application/json' "$CL_URL/v2/bridge_types" &>/dev/null 31 | 32 | echo "Bridges has been added to Chainlink node" 33 | -------------------------------------------------------------------------------- /fm-demo/scripts/add-jobspecs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./scripts/common.sh 4 | 5 | echo "Adding JobSpecs to Chainlink node..." 6 | 7 | CL_URL="http://localhost:6688" 8 | 9 | login_cl "$CL_URL" 10 | 11 | payload=$( 12 | cat <./cookiefile 18 | 19 | curl -s -c ./cookiefile -d "{\"email\":\"${username}\", \"password\":\"${password}\"}" -X POST -H 'Content-Type: application/json' "$CL_URL/sessions" &>/dev/null 20 | } 21 | -------------------------------------------------------------------------------- /fm-demo/scripts/ei-config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Config EI ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | cat < credentials 11 | EOF 12 | docker cp chainlink-node:/home/root/credentials credentials 13 | cat credentials | awk 'NR==5' | awk -F[' '] '{print "EI_IC_ACCESSKEY="$6} {print "EI_IC_SECRET="$8} {print "EI_CI_ACCESSKEY="$10} {print "EI_CI_SECRET="$12}' > external_initiator.env 14 | rm -rf credentials -------------------------------------------------------------------------------- /fm-demo/scripts/run-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Run all components of the integration ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | docker-compose down --remove-orphans --volumes 9 | ./scripts/run-chain.sh 10 | yarn 11 | node ./feedSetup.js 12 | ./scripts/run-chainlink.sh 13 | echo "Waiting a bit for the chainlink services to be ready" 14 | sleep 15 15 | ./scripts/add-bridges.sh 16 | ./scripts/ei-config.sh 17 | ./scripts/run-ei.sh 18 | echo "Waiting a bit for the initiator to be ready" 19 | sleep 10 20 | # ./scripts/add-jobspecs.sh 21 | cd ./go-scripts 22 | go run ./main.go 23 | cd .. 24 | echo "Waiting a bit for the answer to be written on chain" 25 | # TODO: make it variable related to heartbeat 26 | sleep 60 27 | node ./checkAnswer.js 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /fm-demo/scripts/run-chain.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Run substrate chain ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | 9 | docker-compose up -d chain 10 | -------------------------------------------------------------------------------- /fm-demo/scripts/run-chainlink.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Run Chainlink node ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | 9 | docker-compose up -d chainlink substrate-adapter1 substrate-adapter2 adapter1 adapter2 adapter3 10 | -------------------------------------------------------------------------------- /fm-demo/scripts/run-ei.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Run External Initiator ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | 9 | docker-compose up -d external-initiator 10 | -------------------------------------------------------------------------------- /fm-demo/secrets/apicredentials: -------------------------------------------------------------------------------- 1 | notreal@fakeemail.ch 2 | twochains 3 | -------------------------------------------------------------------------------- /fm-demo/secrets/password.txt: -------------------------------------------------------------------------------- 1 | T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ 2 | -------------------------------------------------------------------------------- /fm-demo/specs/spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "initiators": [ 3 | { 4 | "type": "external", 5 | "params": { 6 | "name": "substrate", 7 | "body": { 8 | "endpoint": "substrate", 9 | "feed_id": 0, 10 | "account_id": "0x7c522c8273973e7bcf4a5dbfcc745dba4a3ab08c1e410167d7b1bdf9cb924f6c", 11 | "fluxmonitor": { 12 | "requestData": { 13 | "data": { "from": "DOT", "to": "USD" } 14 | }, 15 | "feeds": [ 16 | { "url": "http://adapter1:8080" }, 17 | { "url": "http://adapter2:8080" }, 18 | { "url": "http://adapter3:8080" }], 19 | "threshold": 0.5, 20 | "absoluteThreshold": 0, 21 | "precision": 8, 22 | "pollTimer": { "period": "5s" }, 23 | "idleTimer": { "duration": "15s" } 24 | } 25 | } 26 | } 27 | } 28 | ], 29 | "tasks": [ 30 | { 31 | "type": "substrate-adapter1", 32 | "params": { "multiply": 1e8 } 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /fm-demo/specs/spec2.json: -------------------------------------------------------------------------------- 1 | { 2 | "initiators": [ 3 | { 4 | "type": "external", 5 | "params": { 6 | "name": "substrate", 7 | "body": { 8 | "endpoint": "substrate", 9 | "feed_id": 0, 10 | "account_id": "0x06f0d58c43477508c0e5d5901342acf93a0208088816ff303996564a1d8c1c54", 11 | "fluxmonitor": { 12 | "requestData": { 13 | "data": { "from": "DOT", "to": "USD" } 14 | }, 15 | "feeds": [ 16 | { "url": "http://adapter1:8080" }, 17 | { "url": "http://adapter2:8080" }, 18 | { "url": "http://adapter3:8080" }], 19 | "threshold": 0.5, 20 | "absoluteThreshold": 0, 21 | "precision": 8, 22 | "pollTimer": { "period": "5s" }, 23 | "idleTimer": { "duration": "15s" } 24 | } 25 | } 26 | } 27 | } 28 | ], 29 | "tasks": [ 30 | { 31 | "type": "substrate-adapter2", 32 | "params": { "multiply": 1e8 } 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /pallet-chainlink-feed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pallet-chainlink-feed" 3 | version = "0.1.0" 4 | authors = ["Parity Technologies "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | homepage = "https://substrate.dev" 8 | repository = "https://github.com/paritytech/substrate/" 9 | description = "chainlink price feed pallet" 10 | readme = "README.md" 11 | 12 | [package.metadata.docs.rs] 13 | targets = ["x86_64-unknown-linux-gnu"] 14 | 15 | [dependencies] 16 | serde = { version = "1.0.125", optional = true, features = ["derive"] } 17 | codec = { package = "parity-scale-codec", version = "2.0.1", features = ['derive'], default-features = false } 18 | sp-arithmetic = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 19 | sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 20 | sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 21 | # Needed for various traits. In our case, `OnFinalize`. 22 | sp-runtime = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 23 | # Needed for type-safe access to storage DB. 24 | frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'master'} 25 | # `system` module provides us with all sorts of useful stuff and macros depend on it being around. 26 | frame-system = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 27 | frame-benchmarking = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'master', optional = true } 28 | 29 | [dev-dependencies] 30 | sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 31 | sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 32 | pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 33 | 34 | [features] 35 | default = ["std"] 36 | std = [ 37 | "serde", 38 | "codec/std", 39 | "sp-std/std", 40 | "sp-core/std", 41 | "sp-runtime/std", 42 | "frame-support/std", 43 | "frame-system/std", 44 | "frame-benchmarking/std", 45 | ] 46 | runtime-benchmarks = [ 47 | "frame-benchmarking", 48 | "sp-runtime/runtime-benchmarks", 49 | "frame-system/runtime-benchmarks", 50 | ] -------------------------------------------------------------------------------- /pallet-chainlink-feed/src/traits.rs: -------------------------------------------------------------------------------- 1 | //! Traits 2 | use crate::{Config, RoundData}; 3 | 4 | /// This implementation wille be as a callback when the round answer updates 5 | pub trait OnAnswerHandler { 6 | fn on_answer(feed: T::FeedId, new_data: RoundData); 7 | } 8 | 9 | impl OnAnswerHandler for () { 10 | fn on_answer(_feed: T::FeedId, _new_data: RoundData) { 11 | // do_nothing 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pallet-chainlink-feed/src/utils.rs: -------------------------------------------------------------------------------- 1 | use frame_support::storage::{with_transaction, TransactionOutcome}; 2 | use sp_arithmetic::traits::BaseArithmetic; 3 | 4 | /// Execute the supplied function in a new storage transaction. 5 | /// 6 | /// All changes to storage performed by the supplied function are discarded if 7 | /// the returned outcome is `Result::Err`. 8 | /// 9 | /// Transactions can be nested to any depth. Commits happen to the parent 10 | /// transaction. 11 | // TODO: remove after move to Substrate v3 (once the semantics of 12 | // #[transactional] work as intended) 13 | pub(crate) fn with_transaction_result(f: impl FnOnce() -> Result) -> Result { 14 | with_transaction(|| { 15 | let res = f(); 16 | if res.is_ok() { 17 | TransactionOutcome::Commit(res) 18 | } else { 19 | TransactionOutcome::Rollback(res) 20 | } 21 | }) 22 | } 23 | 24 | /// Determine the median of a slice of values. 25 | /// 26 | /// **Warning:** Will panic if passed an empty slice. 27 | pub(crate) fn median(numbers: &mut [T]) -> T { 28 | numbers.sort_unstable(); 29 | 30 | let mid = numbers.len() / 2; 31 | if numbers.len() % 2 == 0 { 32 | numbers[mid - 1].saturating_add(numbers[mid]) / 2.into() 33 | } else { 34 | numbers[mid] 35 | } 36 | } 37 | 38 | #[test] 39 | fn median_works() { 40 | let mut values = vec![4u32, 6, 2, 7]; 41 | assert_eq!(median(&mut values), 5); 42 | let mut values = vec![4u32, 6, 2, 7, 9]; 43 | assert_eq!(median(&mut values), 6); 44 | } 45 | 46 | #[test] 47 | #[should_panic] 48 | fn median_panics_on_empty_slice() { 49 | let mut empty: Vec = Vec::new(); 50 | median(&mut empty); 51 | } 52 | -------------------------------------------------------------------------------- /pallet-chainlink/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pallet-chainlink" 3 | version = "2.0.0" 4 | authors = ["Parity Technologies "] 5 | edition = "2018" 6 | 7 | [package.metadata.docs.rs] 8 | targets = ["x86_64-unknown-linux-gnu"] 9 | 10 | [dependencies] 11 | serde = { version = "1.0.125", optional = true } 12 | codec = { package = "parity-scale-codec", version = "2.0.1", features = ['derive'], default-features = false } 13 | sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 14 | sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 15 | sp-runtime = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 16 | frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'master'} 17 | frame-system = { git = 'https://github.com/paritytech/substrate.git', branch = 'master', default-features = false } 18 | 19 | [dev-dependencies] 20 | sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 21 | sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 22 | pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 23 | 24 | [features] 25 | default = ["std"] 26 | std = [ 27 | "serde", 28 | "codec/std", 29 | "sp-std/std", 30 | "sp-runtime/std", 31 | "frame-support/std", 32 | "frame-system/std", 33 | ] 34 | -------------------------------------------------------------------------------- /pallet-chainlink/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 | -------------------------------------------------------------------------------- /pallet-chainlink/README.md: -------------------------------------------------------------------------------- 1 | # Chainlink pallet 2 | 3 | ## Purpose 4 | 5 | This pallet allows your substrate built parachain/blockchain to interract with [chainlink](https://chain.link/). [Pallets](https://substrate.dev/docs/en/tutorials/build-a-dapp/pallet) are modular pieces of the Polkadot Substrate that make it easier for your parachain to interact with technologies. This is essential for working with any kind of external data API from outside your blockchain. 6 | 7 | Essentially, a pallet is a lego piece you can add to another blockchain built on the Substrate/Polkadot infrastructure. 8 | 9 | 10 | ## Installation 11 | 12 | Using `pallet-chainlink` is fairly straightforward and requires a couple easy steps: 13 | 14 | - add the correct dependency to your runtime 15 | - use some of the pallet bundled functions 16 | 17 | ### Add the pallet dependency 18 | 19 | Update `Cargo.toml` to reference `pallet-chainlink`. 20 | 21 | Add the following section: 22 | 23 | ```toml 24 | [dependencies.chainlink] 25 | default_features = false 26 | package = 'pallet-chainlink' 27 | git = 'https://github.com/smartcontractkit/chainlink-polkadot.git' 28 | ``` 29 | 30 | And amend the `std` section so that it shows like this: 31 | 32 | ```toml 33 | std = [ 34 | ... // all the existing pallets 35 | 'chainlink/std' 36 | ] 37 | ``` 38 | 39 | ### Use provided functions 40 | 41 | Edit `lib.rs` to that it references `pallet-chainlink`: 42 | 43 | ```rust 44 | ... 45 | // Add the chainlink Config Trait 46 | impl chainlink::Config for Runtime { 47 | type Event = Event; 48 | type Currency = balances::Module; 49 | type Callback = example_module::Call; 50 | type ValidityPeriod = ValidityPeriod; 51 | } 52 | 53 | parameter_types! { 54 | pub const ValidityPeriod: u32 = 50; 55 | } 56 | ... 57 | // In construct_runtime!, add the pallet 58 | ... 59 | construct_runtime!( 60 | ... 61 | Chainlink: chainlink::{Pallet, Call, Storage, Event}, 62 | } 63 | ); 64 | ``` 65 | 66 | Add necessary `use` declarations: 67 | 68 | ```rust 69 | use chainlink::{CallbackWithParameter, Event, Config as ChainlinkConfig}; 70 | 71 | pub trait Config: chainlink::Config + ChainlinkTrait { 72 | type Event: From> + Into<::Event>; 73 | type Callback: From> + Into<::Callback>; 74 | } 75 | ``` 76 | 77 | You can now call the right chainlink Extrinsic: 78 | 79 | ```rust 80 | pub fn send_request(origin, operator: T::AccountId) -> DispatchResult { 81 | let parameters = ("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD", "path", "RAW.ETH.USD.PRICE", "times", "100000000"); 82 | let call: ::Callback = Call::callback(vec![]).into(); 83 | chainlink::Pallet::::initiate_request(origin, operator, 1, 0, parameters.encode(), 100, call.into())?; 84 | 85 | Ok(()) 86 | } 87 | ``` 88 | 89 | This call refers to a callback Extrinsic that mut be define in the pallet. It will receive back the chainlink Operator's result: 90 | 91 | ```rust 92 | pub fn callback(origin, result: u128) -> DispatchResult { 93 | ensure_root(origin)?; 94 | 95 | let r : u128 = u128::decode(&mut &result[..]).map_err(|err| err.what())?; 96 | ::put(r); 97 | Ok(()) 98 | } 99 | 100 | impl CallbackWithParameter for Call { 101 | fn with_result(&self, result: u128) -> Option { 102 | match *self { 103 | Call::callback(_) => Some(Call::callback(result)), 104 | _ => None 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | Under the hood a specific event will be picked up by chainlink nodes that will in turn call be a well-known Extrinsic. 111 | 112 | ### Genesis Configuration 113 | 114 | This template pallet does not have any genesis configuration. 115 | 116 | ## Reference Docs 117 | 118 | You can view the reference docs for this pallet by running: 119 | 120 | ``` 121 | cargo doc --open 122 | ``` 123 | -------------------------------------------------------------------------------- /runlog-demo/.gitignore: -------------------------------------------------------------------------------- 1 | tmp/ 2 | external_initiator1.env 3 | external_initiator2.env 4 | external_initiator3.env 5 | node_modules/ 6 | jobids.txt 7 | -------------------------------------------------------------------------------- /runlog-demo/README.md: -------------------------------------------------------------------------------- 1 | # Chainlink components for Substrate 2 | 3 | This tool automates the setup and running of Chainlink components to read/write from a Substrate chain. 4 | 5 | ## Running 6 | 7 | ### Initial setup 8 | 9 | _Note: Make sure you have cd-ed into this directory_ 10 | 11 | ```bash 12 | ./setup 13 | ``` 14 | 15 | This will create and start 3 Chainlink nodes, with an adapter and EI connected to each. 16 | 17 | Also it will spin up a substrate node with the RunLog pallet included. (This node is built locally and might take some time initially. You will see a message of "API-WS: disconnected from ws://localhost:9944: 1006:: connection failed" while the node is being built. You can check the status of it by running "docker attach chain-runlog"). 18 | 19 | ### Start/stop 20 | 21 | To stop the nodes, run: 22 | 23 | ```bash 24 | docker-compose down 25 | ``` 26 | 27 | And to start them again, run: 28 | 29 | ```bash 30 | docker-compose up 31 | ``` 32 | 33 | ## Troubleshooting 34 | 35 | ### Stuck at "waiting for localhost:669X" 36 | 37 | Check the logs of your docker container running the chainlink node: 38 | `docker logs -f runlog-demo_chainlink-node1_1` 39 | 40 | You need to make sure you followed the setup section 41 | 42 | ### cat jobids.txt is null 43 | 44 | The external initiator needs to be up and running before you can create jobs. 45 | 46 | It might be the case that it wasn't operational yet, in this case simply re-execute the 47 | part of job creation from setup: 48 | 49 | ```bash 50 | source ./internal-scripts/add-jobspec.sh 51 | 52 | add_jobspec ... 53 | ``` 54 | -------------------------------------------------------------------------------- /runlog-demo/adapter.env: -------------------------------------------------------------------------------- 1 | SA_TX_TYPE=immortal 2 | -------------------------------------------------------------------------------- /runlog-demo/chainlink.env: -------------------------------------------------------------------------------- 1 | ROOT=/chainlink 2 | LOG_LEVEL=debug 3 | ETH_CHAIN_ID=1 4 | MIN_OUTGOING_CONFIRMATIONS=2 5 | CHAINLINK_TLS_PORT=0 6 | SECURE_COOKIES=false 7 | ALLOW_ORIGINS=* 8 | CHAINLINK_DEV=true 9 | ETH_DISABLED=true 10 | -------------------------------------------------------------------------------- /runlog-demo/docker-init-scripts/create-multiple-databases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -u 5 | 6 | function create_user_and_database() { 7 | local database=$1 8 | echo " Creating user and database '$database'" 9 | psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL 10 | CREATE USER $database; 11 | CREATE DATABASE $database; 12 | GRANT ALL PRIVILEGES ON DATABASE $database TO $database; 13 | EOSQL 14 | } 15 | 16 | if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then 17 | echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" 18 | for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do 19 | create_user_and_database $db 20 | done 21 | echo "Multiple databases created" 22 | fi 23 | -------------------------------------------------------------------------------- /runlog-demo/internal-scripts/add-bridge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./internal-scripts/common.sh 4 | 5 | add_bridge() { 6 | title "Adding External Adapter #$1 to Chainlink node..." 7 | 8 | CL_URL="http://localhost:669$1" 9 | 10 | login_cl "$CL_URL" 11 | 12 | payload=$( 13 | cat </dev/null 22 | 23 | echo "EA has been added to Chainlink node" 24 | title "Done adding EA #$1" 25 | } 26 | -------------------------------------------------------------------------------- /runlog-demo/internal-scripts/add-ei.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./internal-scripts/common.sh 4 | 5 | add_ei() { 6 | title "Adding External Initiator #$1 to Chainlink node..." 7 | 8 | CL_URL="http://localhost:669$1" 9 | 10 | login_cl "$CL_URL" 11 | 12 | payload=$( 13 | cat <> jobids.txt 52 | 53 | echo "Jobspec has been added to Chainlink node" 54 | title "Done adding jobspec #$1" 55 | } 56 | -------------------------------------------------------------------------------- /runlog-demo/internal-scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOG_PATH=./tmp/logs 4 | mkdir -p "$LOG_PATH" 5 | 6 | waitFor() { 7 | [ -z "$2" ] && timeout=60 || timeout=$2 8 | sleepCount=0 9 | while [ "$sleepCount" -le "$timeout" ] && ! eval "$1" >/dev/null; do 10 | sleep 1 11 | sleepCount=$((sleepCount + 1)) 12 | done 13 | 14 | if [ "$sleepCount" -gt "$timeout" ]; then 15 | printf -- "\033[31mTimed out waiting for '%s' (waited %ss).\033[0m\n" "$1" "${timeout}" 16 | exit 1 17 | fi 18 | } 19 | 20 | waitForResponse() { 21 | title "Waiting for $1." 22 | waitFor "curl -s \"$1\"" 23 | title "Service on $1 is ready." 24 | } 25 | 26 | launch_chainlink() { 27 | waitForResponse "$1" 28 | title "Chainlink node $1 is running." 29 | } 30 | 31 | login_cl() { 32 | CL_URL=$1 33 | 34 | username="" 35 | password="" 36 | 37 | while IFS= read -r line; do 38 | if [[ "$username" == "" ]]; then 39 | username=${line} 40 | else 41 | password=${line} 42 | fi 43 | done <"./secrets/apicredentials" 44 | 45 | echo "" >./cookiefile 46 | 47 | curl -s -c ./cookiefile -d "{\"email\":\"${username}\", \"password\":\"${password}\"}" -X POST -H 'Content-Type: application/json' "$CL_URL/sessions" &>/dev/null 48 | } 49 | 50 | run_ei() { 51 | title "Running External Initiator #$1..." 52 | 53 | EI_CI_ACCESSKEY=$2 54 | EI_CI_SECRET=$3 55 | EI_IC_ACCESSKEY=$4 56 | EI_IC_SECRET=$5 57 | 58 | if [ "$EI_CI_ACCESSKEY" != "" ]; then 59 | { 60 | echo "EI_CI_ACCESSKEY=$EI_CI_ACCESSKEY" 61 | echo "EI_CI_SECRET=$EI_CI_SECRET" 62 | echo "EI_IC_ACCESSKEY=$EI_IC_ACCESSKEY" 63 | echo "EI_IC_SECRET=$EI_IC_SECRET" 64 | } >"external_initiator$1.env" 65 | fi 66 | 67 | docker-compose up -d "external-initiator-node$1" 68 | } 69 | 70 | start_docker() { 71 | title "Starting Chainlink Docker containers" 72 | 73 | docker-compose up -d chain-runlog chainlink-node1 chainlink-node2 chainlink-node3 substrate-adapter1 substrate-adapter2 substrate-adapter3 74 | 75 | launch_chainlink "http://localhost:6691/" 76 | launch_chainlink "http://localhost:6692/" 77 | launch_chainlink "http://localhost:6693/" 78 | 79 | title "Done starting Chainlink Docker containers" 80 | } 81 | 82 | stop_docker() { 83 | title "Stopping Docker containers" 84 | 85 | docker-compose down 86 | 87 | title "Done stopping Docker containers" 88 | } 89 | 90 | build_docker() { 91 | title "Building Docker images" 92 | 93 | docker-compose build 94 | 95 | title "Done building Docker images" 96 | } 97 | 98 | reset_volumes() { 99 | title "Removing Docker volumes" 100 | 101 | docker volume rm runlog-demo_cl1 102 | docker volume rm runlog-demo_cl2 103 | docker volume rm runlog-demo_cl3 104 | docker volume rm runlog-demo_pg1 105 | docker volume rm runlog-demo_pg2 106 | docker volume rm runlog-demo_pg3 107 | 108 | title "Done removing Docker volumes" 109 | } 110 | 111 | print_logs() { 112 | for log in $(find "$LOG_PATH" -maxdepth 1 -type f -iname '*.log'); do 113 | heading "$log" 114 | cat "$log" 115 | done 116 | } 117 | 118 | exit_handler() { 119 | errno=$? 120 | # Print all the logs if the test fails 121 | if [ $errno -ne 0 ]; then 122 | title "ABORTING TEST" 123 | printf -- "Exited with code %s\n" "$errno" 124 | print_logs 125 | fi 126 | exit $errno 127 | } 128 | 129 | title() { 130 | printf -- "\033[34m%s\033[0m\n" "$1" 131 | } 132 | 133 | heading() { 134 | printf -- "\n--------------------------------------------------------------------------------\n" 135 | title "$1" 136 | printf -- "--------------------------------------------------------------------------------\n\n" 137 | } 138 | 139 | -------------------------------------------------------------------------------- /runlog-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "runlog-demo", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "@polkadot/api": "^4.10.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /runlog-demo/secrets/apicredentials: -------------------------------------------------------------------------------- 1 | notreal@fakeemail.ch 2 | twochains 3 | -------------------------------------------------------------------------------- /runlog-demo/secrets/password.txt: -------------------------------------------------------------------------------- 1 | T.tLHkcmwePT/p,]sYuntjwHKAsrhm#4eRs4LuKHwvHejWYAC2JP4M8HimwgmbaZ -------------------------------------------------------------------------------- /runlog-demo/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./internal-scripts/common.sh 4 | 5 | run_setup() { 6 | yarn install 7 | rm -rf external_initiator1.env 8 | rm -rf external_initiator2.env 9 | rm -rf external_initiator3.env 10 | touch external_initiator1.env 11 | touch external_initiator2.env 12 | touch external_initiator3.env 13 | 14 | SA_ENDPOINT="ws://chain-runlog:9944/" 15 | 16 | if [ "$1" != "" ]; then 17 | SA_ENDPOINT=$1 18 | fi 19 | 20 | echo "Endpoint: $SA_ENDPOINT" 21 | 22 | export SA_ENDPOINT 23 | start_docker 24 | 25 | source ./internal-scripts/add-ei.sh 26 | 27 | add_ei "1" 28 | add_ei "2" 29 | add_ei "3" 30 | 31 | source ./internal-scripts/add-bridge.sh 32 | 33 | add_bridge "1" 34 | add_bridge "2" 35 | add_bridge "3" 36 | 37 | echo "" > jobids.txt 38 | 39 | echo "Waiting a bit for the external initiators to be ready" 40 | sleep 10 41 | 42 | source ./internal-scripts/add-jobspec.sh 43 | 44 | add_jobspec "1" "0x7c522c8273973e7bcf4a5dbfcc745dba4a3ab08c1e410167d7b1bdf9cb924f6c" 45 | add_jobspec "2" "0x06f0d58c43477508c0e5d5901342acf93a0208088816ff303996564a1d8c1c54" 46 | add_jobspec "3" "0xfaa31acde43e8859565f7576d5a37e6e8ee1b0f6a7c1ae2e8b0ce2bf76248467" 47 | 48 | title "Done setting up Chainlink nodes and services" 49 | title "Funding accounts and registering operators..." 50 | 51 | node ./operatorSetup.js "dry squeeze youth enjoy provide blouse claw engage host what horn next" 52 | node ./operatorSetup.js "price trip nominee recycle walk park borrow sausage crucial only wheel joke" 53 | node ./operatorSetup.js "camp acid then kid between survey dentist delay actor fox ensure soccer" 54 | 55 | title "All operators are now ready" 56 | echo "Jobids can be found in jobids.txt or their respective Chainlink GUIs" 57 | } 58 | 59 | run_setup "$1" 60 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | max_width = 100 3 | -------------------------------------------------------------------------------- /substrate-node-example/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Substrate Node template", 3 | "context": "..", 4 | "settings": { 5 | "terminal.integrated.shell.linux": "/bin/bash", 6 | "lldb.executable": "/usr/bin/lldb" 7 | }, 8 | "extensions": [ 9 | "rust-lang.rust", 10 | "bungcip.better-toml", 11 | "vadimcn.vscode-lldb" 12 | ], 13 | "forwardPorts": [ 14 | 3000, 15 | 9944 16 | ], 17 | "preCreateCommand": "cargo build --release && cargo check", 18 | "postStartCommand": "./target/release/node-template --dev --ws-external", 19 | "menuActions": [ 20 | {"id": "polkadotjs", 21 | "label": "Open PolkadotJS Apps", 22 | "type": "external-preview", 23 | "args": ["https://polkadot.js.org/apps/?rpc=wss%3A%2F%2F/$HOST/wss"]} 24 | ], 25 | "image": "paritytech/substrate-playground-template-node-template:latest" 26 | } 27 | -------------------------------------------------------------------------------- /substrate-node-example/.github/ISSUE_TEMPLATE/ask-a-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask a Question 3 | about: Ask a question about this template. 4 | title: "" 5 | labels: question 6 | assignees: "" 7 | --- 8 | 9 | **Question** 10 | 11 | _Please include information such as the following: is your question to clarify an existing resource 12 | or are you asking about something new? what are you trying to accomplish? where have you looked for 13 | answers?_ 14 | -------------------------------------------------------------------------------- /substrate-node-example/.github/ISSUE_TEMPLATE/report-a-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a Bug 3 | about: Report a problem with this template. 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Description** 10 | 11 | _Tell us what happened. In particular, be specific about any changes you made to this template. 12 | Ideally, provide a link to your project's GitHub repository. Please note that we are not able to 13 | support all conceivable changes to this template project, but the more information you are able to 14 | provide the more equipped we will be to help._ 15 | 16 | **Steps to Reproduce** 17 | 18 | _Replace the example steps below with actual steps to reproduce the bug you're reporting._ 19 | 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected vs. Actual Behavior** 26 | 27 | _What did you expect to happen after you followed the steps you described in the last section? What 28 | actually happened?_ 29 | 30 | **Environment** 31 | 32 | _Describe the environment in which you encountered this bug. Use the list below as a starting point 33 | and add additional information if you think it's relevant._ 34 | 35 | - Operating system: 36 | - Template version/tag: 37 | - Rust version (run `rustup show`): 38 | 39 | **Logs, Errors or Screenshots** 40 | 41 | _Please provide the text of any logs or errors that you experienced; if 42 | applicable, provide screenshots to help illustrate the problem._ 43 | 44 | **Additional Information** 45 | 46 | _Please add any other details that you think may help us solve your problem._ 47 | -------------------------------------------------------------------------------- /substrate-node-example/.github/ISSUE_TEMPLATE/suggest-a-feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Suggest a Feature 3 | about: Suggest a new feature or an improvement to an existing feature for this template. 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **Motivation** 10 | 11 | _Describe the need or frustration that motivated you to make this suggestion. Please note that the 12 | goal of this project is to provide a general-purpose template project, so please take care when 13 | suggesting features that may be specific to a particular use case._ 14 | 15 | **Suggested Solution** 16 | 17 | _Describe your suggested solution to the need or frustration that you are experiencing._ 18 | 19 | **Alternatives** 20 | 21 | _Describe any alternative solutions or features you considered and why you believe your suggested 22 | solution is preferable._ 23 | 24 | **Additional Information** 25 | 26 | _Provide any additional information that you believe may help us evaluate your suggestion._ 27 | -------------------------------------------------------------------------------- /substrate-node-example/.github/workflows/build-push-template.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push template 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build-push-template: 10 | if: ${{ github.repository == 'substrate-developer-hub/substrate-node-template' }} 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - name: Trigger playground inclusion 14 | uses: peter-evans/repository-dispatch@v1 15 | with: 16 | token: ${{ secrets.REPO_ACCESS_TOKEN }} 17 | repository: paritytech/substrate-playground 18 | event-type: template-updated 19 | client-payload: '{"id": "node-template"}' 20 | -------------------------------------------------------------------------------- /substrate-node-example/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | # These are backup files generated by rustfmt 5 | **/*.rs.bk 6 | 7 | .DS_Store 8 | 9 | # The cache for docker container dependency 10 | .cargo 11 | 12 | # The cache for chain data in container 13 | .local -------------------------------------------------------------------------------- /substrate-node-example/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Run ", 8 | "type": "shell", 9 | "command": "cargo", 10 | "args": ["run", "--release", "--", "--dev"], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | }, 15 | "presentation": { 16 | "reveal": "always", 17 | "panel": "new" 18 | }, 19 | "problemMatcher": [ 20 | { 21 | "owner": "rust", 22 | "fileLocation": ["relative", "${workspaceRoot}"], 23 | "pattern": { 24 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(\\d+):(\\d+)\\s+(warning|error):\\s+(.*)$", 25 | "file": 1, 26 | "line": 2, 27 | "column": 3, 28 | "endLine": 4, 29 | "endColumn": 5, 30 | "severity": 6, 31 | "message": 7 32 | } 33 | } 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /substrate-node-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.release] 2 | panic = 'unwind' 3 | 4 | [workspace] 5 | members = [ 6 | 'node', 7 | 'pallets/*', 8 | 'runtime', 9 | ] 10 | -------------------------------------------------------------------------------- /substrate-node-example/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 | -------------------------------------------------------------------------------- /substrate-node-example/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: init 2 | init: 3 | ./scripts/init.sh 4 | 5 | .PHONY: check 6 | check: 7 | SKIP_WASM_BUILD=1 cargo check --release 8 | 9 | .PHONY: test 10 | test: 11 | SKIP_WASM_BUILD=1 cargo test --release --all 12 | 13 | .PHONY: run 14 | run: 15 | cargo run --release -- --dev --tmp 16 | 17 | .PHONY: build 18 | build: 19 | cargo build --release 20 | -------------------------------------------------------------------------------- /substrate-node-example/doc/rust-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | --- 4 | 5 | This page will guide you through the steps needed to prepare a computer for development with the 6 | Substrate Node Template. Since Substrate is built with 7 | [the Rust programming language](https://www.rust-lang.org/), the first thing you will need to do is 8 | prepare the computer for Rust development - these steps will vary based on the computer's operating 9 | system. Once Rust is configured, you will use its toolchains to interact with Rust projects; the 10 | commands for Rust's toolchains will be the same for all supported, Unix-based operating systems. 11 | 12 | ## Unix-Based Operating Systems 13 | 14 | Substrate development is easiest on Unix-based operating systems like macOS or Linux. The examples 15 | in the Substrate [Tutorials](https://substrate.dev/tutorials) and [Recipes](https://substrate.dev/recipes/) 16 | use Unix-style terminals to demonstrate how to interact with Substrate from the command line. 17 | 18 | ### macOS 19 | 20 | Open the Terminal application and execute the following commands: 21 | 22 | ```bash 23 | # Install Homebrew if necessary https://brew.sh/ 24 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 25 | 26 | # Make sure Homebrew is up-to-date, install openssl and cmake 27 | brew update 28 | brew install openssl cmake 29 | ``` 30 | 31 | ### Ubuntu/Debian 32 | 33 | Use a terminal shell to execute the following commands: 34 | 35 | ```bash 36 | sudo apt update 37 | # May prompt for location information 38 | sudo apt install -y cmake pkg-config libssl-dev git build-essential clang libclang-dev curl 39 | ``` 40 | 41 | ### Arch Linux 42 | 43 | Run these commands from a terminal: 44 | 45 | ```bash 46 | pacman -Syu --needed --noconfirm cmake gcc openssl-1.0 pkgconf git clang 47 | export OPENSSL_LIB_DIR="/usr/lib/openssl-1.0" 48 | export OPENSSL_INCLUDE_DIR="/usr/include/openssl-1.0" 49 | ``` 50 | 51 | ## Rust Developer Environment 52 | 53 | This project uses [`rustup`](https://rustup.rs/) to help manage the Rust toolchain. First install 54 | and configure `rustup`: 55 | 56 | ```bash 57 | # Install 58 | curl https://sh.rustup.rs -sSf | sh 59 | # Configure 60 | source ~/.cargo/env 61 | ``` 62 | 63 | Finally, configure the Rust toolchain to default to the latest stable version: 64 | 65 | ```bash 66 | rustup default stable 67 | ``` 68 | 69 | ## Build the Project 70 | 71 | Now that the standard Rust environment is configured, use the 72 | [included Makefile](../README.md#makefile) to install the project-specific toolchains and build the 73 | project. 74 | -------------------------------------------------------------------------------- /substrate-node-example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.2" 2 | 3 | services: 4 | dev: 5 | container_name: node-template 6 | image: paritytech/ci-linux:974ba3ac-20201006 7 | working_dir: /var/www/node-template 8 | ports: 9 | - "9944:9944" 10 | environment: 11 | - CARGO_HOME=/var/www/node-template/.cargo 12 | volumes: 13 | - .:/var/www/node-template 14 | - ../pallet-chainlink-feed:/var/www/pallet-chainlink-feed 15 | - type: bind 16 | source: ./.local 17 | target: /root/.local 18 | command: bash -c "cargo build --release && ./target/release/node-template --dev --ws-external" 19 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | 6 | aliases: 7 | - &filter-non-gh-pages 8 | branches: 9 | ignore: 10 | - gh-pages 11 | - &filter-only-master 12 | branches: 13 | only: 14 | - master 15 | 16 | version: 2 17 | jobs: 18 | test: 19 | docker: 20 | - image: cimg/node:14.16 21 | working_directory: ~/repo 22 | steps: 23 | - checkout 24 | # Download and cache dependencies 25 | - restore_cache: 26 | keys: 27 | - v1-dependencies-{{ checksum "package.json" }} 28 | # fallback to using the latest cache if no exact match is found 29 | - v1-dependencies- 30 | - run: 31 | name: Install 32 | command: | 33 | sudo apt-get -y update 34 | yarn install 35 | - save_cache: 36 | paths: 37 | - node_modules 38 | key: v1-dependencies-{{ checksum "package.json" }} 39 | - run: 40 | name: Syntax Lint 41 | command: yarn lint:ci 42 | - run: 43 | name: Test 44 | command: yarn test 45 | - persist_to_workspace: 46 | # relative to working_directory 47 | root: "./" 48 | paths: 49 | - "./" 50 | 51 | deploy: 52 | docker: 53 | - image: cimg/node:14.16 54 | working_directory: ~/repo 55 | steps: 56 | - attach_workspace: 57 | at: ~/repo 58 | - run: 59 | name: Build & Deploy 60 | # added skip host key checking to avoid the manual prompt 61 | command: | 62 | mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config 63 | git config --global user.email "devhub-deploy@users.noreply.github.com" 64 | git config --global user.name "Devhub Deployer" 65 | yarn deploy 66 | 67 | workflows: 68 | version: 2 69 | test_deploy: 70 | jobs: 71 | - test: 72 | filters: *filter-non-gh-pages 73 | - deploy: 74 | filters: *filter-only-master 75 | requires: 76 | - test 77 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/.env: -------------------------------------------------------------------------------- 1 | EXTEND_ESLINT=true 2 | PORT=8000 3 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | **/.DS_Store 4 | build 5 | .eslintcache 6 | # Setting for non zero-installs (https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored) 7 | .yarn/* 8 | !.yarn/releases 9 | !.yarn/plugins 10 | !.yarn/sdks 11 | !.yarn/versions 12 | .pnp.* 13 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/fermium 2 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 5 | spec: "@yarnpkg/plugin-interactive-tools" 6 | 7 | yarnPath: .yarn/releases/yarn-berry.cjs 8 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/config-overrides.js: -------------------------------------------------------------------------------- 1 | module.exports = function override(webpackConfig) { 2 | webpackConfig.module.rules.push({ 3 | test: /\.mjs$/, 4 | include: /node_modules/, 5 | type: "javascript/auto" 6 | }); 7 | 8 | return webpackConfig; 9 | } 10 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | "scripts": { 8 | "start": "react-app-rewired start", 9 | "build": "react-app-rewired build", 10 | "test": "CI=true react-app-rewired test --env=jsdom", 11 | "eject": "react-app-rewired eject", 12 | "lint": "eslint src/**/*.js", 13 | "lint:ci": "eslint src/**/*.js --max-warnings=0", 14 | "lint:fix": "eslint --fix src/**/*.js", 15 | "predeploy": "yarn build", 16 | "deploy": "gh-pages -d build -m '[ci skip] Updates'" 17 | }, 18 | "dependencies": { 19 | "@polkadot/api": "^4.8.1", 20 | "@polkadot/extension-dapp": "^0.38.1", 21 | "@polkadot/keyring": "^6.3.1", 22 | "@polkadot/networks": "^6.3.1", 23 | "@polkadot/types": "^4.8.1", 24 | "@polkadot/ui-keyring": "^0.75.1", 25 | "@polkadot/ui-settings": "^0.75.1", 26 | "@polkadot/util": "^6.3.1", 27 | "@polkadot/util-crypto": "^6.3.1", 28 | "prop-types": "^15.7.2", 29 | "query-string": "^6.13.5", 30 | "react": "^16.14.0", 31 | "react-copy-to-clipboard": "^5.0.3", 32 | "react-dom": "^16.14.0", 33 | "react-scripts": "^4.0.3", 34 | "semantic-ui-css": "^2.4.1", 35 | "semantic-ui-react": "^2.0.3" 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "^7.14.3", 39 | "babel-preset-env": "^1.7.0", 40 | "eslint": "^7.20.0", 41 | "eslint-config-semistandard": "^15.0.1", 42 | "eslint-config-standard": "^16.0.2", 43 | "eslint-plugin-import": "^2.22.1", 44 | "eslint-plugin-node": "^11.1.0", 45 | "eslint-plugin-only-warn": "^1.0.2", 46 | "eslint-plugin-promise": "^4.3.1", 47 | "eslint-plugin-standard": "^5.0.0", 48 | "gh-pages": "^3.1.0", 49 | "react-app-rewired": "^2.1.8", 50 | "semistandard": "^16.0.0" 51 | }, 52 | "eslintConfig": { 53 | "extends": [ 54 | "react-app", 55 | "semistandard" 56 | ], 57 | "plugins": [ 58 | "only-warn" 59 | ] 60 | }, 61 | "engines": { 62 | "node": ">=12", 63 | "npm": ">=6" 64 | }, 65 | "browserslist": { 66 | "production": [ 67 | ">0.2%", 68 | "not dead", 69 | "not op_mini all" 70 | ], 71 | "development": [ 72 | "last 1 chrome version", 73 | "last 1 firefox version", 74 | "last 1 safari version" 75 | ] 76 | }, 77 | "homepage": "https://substrate-developer-hub.github.io/substrate-front-end-template", 78 | "bugs": { 79 | "url": "https://github.com/substrate-developer-hub/substrate-front-end-template/issues" 80 | }, 81 | "keywords": [ 82 | "substrate", 83 | "substrate-ui", 84 | "polkadot-js" 85 | ], 86 | "jest": { 87 | "modulePathIgnorePatterns": [ 88 | "/src/__tests__/mock.js" 89 | ], 90 | "moduleNameMapper": { 91 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.js", 92 | "\\.(css|less)$": "/src/__tests__/mock.js" 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/public/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartcontractkit/chainlink-polkadot/215c22351856aac1dca82d9249b33a5bf209eb32/substrate-node-example/front-end/public/assets/favicon.ico -------------------------------------------------------------------------------- /substrate-node-example/front-end/public/assets/main.css: -------------------------------------------------------------------------------- 1 | .github-fork-ribbon.right-bottom:before { 2 | background-color: #222 3 | } 4 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/public/assets/substrate-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartcontractkit/chainlink-polkadot/215c22351856aac1dca82d9249b33a5bf209eb32/substrate-node-example/front-end/public/assets/substrate-logo.png -------------------------------------------------------------------------------- /substrate-node-example/front-end/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | Substrate Front End Template 27 | 28 | 29 | 30 |
31 | 41 | Fork me on GitHub 42 | 43 | 44 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, createRef } from 'react'; 2 | import { Container, Dimmer, Loader, Grid, Sticky, Message } from 'semantic-ui-react'; 3 | import 'semantic-ui-css/semantic.min.css'; 4 | 5 | import { SubstrateContextProvider, useSubstrate } from './substrate-lib'; 6 | import { DeveloperConsole } from './substrate-lib/components'; 7 | 8 | import AccountSelector from './AccountSelector'; 9 | import Balances from './Balances'; 10 | import BlockNumber from './BlockNumber'; 11 | import Events from './Events'; 12 | import ChainlinkExample from './ChainlinkExample'; 13 | import Interactor from './Interactor'; 14 | import Metadata from './Metadata'; 15 | import NodeInfo from './NodeInfo'; 16 | import TemplateModule from './TemplateModule'; 17 | import Transfer from './Transfer'; 18 | import Upgrade from './Upgrade'; 19 | 20 | function Main () { 21 | const [accountAddress, setAccountAddress] = useState(null); 22 | const { apiState, keyring, keyringState, apiError } = useSubstrate(); 23 | const accountPair = 24 | accountAddress && 25 | keyringState === 'READY' && 26 | keyring.getPair(accountAddress); 27 | 28 | const loader = text => 29 | 30 | {text} 31 | ; 32 | 33 | const message = err => 34 | 35 | 36 | 40 | 41 | ; 42 | 43 | if (apiState === 'ERROR') return message(apiError); 44 | else if (apiState !== 'READY') return loader('Connecting to Substrate'); 45 | 46 | if (keyringState !== 'READY') { 47 | return loader('Loading accounts (please review any extension\'s authorization)'); 48 | } 49 | 50 | const contextRef = createRef(); 51 | 52 | return ( 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | ); 87 | } 88 | 89 | export default function App () { 90 | return ( 91 | 92 |
93 | 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/Balances.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Table, Grid, Button } from 'semantic-ui-react'; 3 | import { CopyToClipboard } from 'react-copy-to-clipboard'; 4 | import { useSubstrate } from './substrate-lib'; 5 | 6 | export default function Main (props) { 7 | const { api, keyring } = useSubstrate(); 8 | const accounts = keyring.getPairs(); 9 | const [balances, setBalances] = useState({}); 10 | 11 | useEffect(() => { 12 | const addresses = keyring.getPairs().map(account => account.address); 13 | let unsubscribeAll = null; 14 | 15 | api.query.system.account 16 | .multi(addresses, balances => { 17 | const balancesMap = addresses.reduce((acc, address, index) => ({ 18 | ...acc, [address]: balances[index].data.free.toHuman() 19 | }), {}); 20 | setBalances(balancesMap); 21 | }).then(unsub => { 22 | unsubscribeAll = unsub; 23 | }).catch(console.error); 24 | 25 | return () => unsubscribeAll && unsubscribeAll(); 26 | }, [api, keyring, setBalances]); 27 | 28 | return ( 29 | 30 |

Balances

31 | 32 | 33 | 34 | 35 | Name 36 | 37 | 38 | Address 39 | 40 | 41 | Balance 42 | 43 | 44 | {accounts.map(account => 45 | 46 | {account.meta.name} 47 | 48 | 49 | {account.address} 50 | 51 | 52 |
70 |
71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | function Main (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 | }) 23 | .then(unsub => { 24 | unsubscribeAll = unsub; 25 | }) 26 | .catch(console.error); 27 | 28 | return () => unsubscribeAll && unsubscribeAll(); 29 | }, [bestNumber]); 30 | 31 | const timer = () => { 32 | setBlockNumberTimer(time => time + 1); 33 | }; 34 | 35 | useEffect(() => { 36 | const id = setInterval(timer, 1000); 37 | return () => clearInterval(id); 38 | }, []); 39 | 40 | return ( 41 | 42 | 43 | 44 | 48 | 49 | 50 | {blockNumberTimer} 51 | 52 | 53 | 54 | ); 55 | } 56 | 57 | export default function BlockNumber (props) { 58 | const { api } = useSubstrate(); 59 | return api.derive && 60 | api.derive.chain && 61 | api.derive.chain.bestNumber && 62 | api.derive.chain.bestNumberFinalized 63 | ?
64 | : null; 65 | } 66 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/ChainlinkExample.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Form, Grid, 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 [status, setStatus] = useState(null); 9 | const [formState, setFormState] = useState({ addressTo: null, specId: null }); 10 | const { accountPair } = props; 11 | 12 | const onChange = (_, data) => 13 | setFormState(prevState => ({ ...formState, [data.state]: data.value })); 14 | 15 | const { addressTo, specId } = formState; 16 | 17 | return ( 18 | 19 |

Chainlink

20 |
21 | 22 | 26 | 27 | 28 | 32 | 33 | 34 | 46 | 47 |
{status}
48 |
49 |
50 | ); 51 | } 52 | 53 | export default function ChainlinkExample (props) { 54 | const { api } = useSubstrate(); 55 | return (api.query.balances && api.tx.balances.transfer 56 | ?
57 | : null); 58 | } 59 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/Events.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Feed, Grid, Button } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | 6 | // Events to be filtered from feed 7 | const FILTERED_EVENTS = [ 8 | 'system:ExtrinsicSuccess:: (phase={"ApplyExtrinsic":0})', 9 | 'system:ExtrinsicSuccess:: (phase={"ApplyExtrinsic":1})' 10 | ]; 11 | 12 | function Main (props) { 13 | const { api } = useSubstrate(); 14 | const [eventFeed, setEventFeed] = useState([]); 15 | 16 | useEffect(() => { 17 | let unsub = null; 18 | const allEvents = async () => { 19 | unsub = await api.query.system.events(events => { 20 | // loop through the Vec 21 | events.forEach(record => { 22 | // extract the phase, event and the event types 23 | const { event, phase } = record; 24 | const types = event.typeDef; 25 | 26 | // show what we are busy with 27 | const eventName = `${event.section}:${ 28 | event.method 29 | }:: (phase=${phase.toString()})`; 30 | 31 | if (FILTERED_EVENTS.includes(eventName)) return; 32 | 33 | // loop through each of the parameters, displaying the type and data 34 | const params = event.data.map( 35 | (data, index) => `${types[index].type}: ${data.toString()}` 36 | ); 37 | 38 | setEventFeed(e => [{ 39 | icon: 'bell', 40 | summary: `${eventName}-${e.length}`, 41 | extraText: event.meta.documentation.join(', ').toString(), 42 | content: params.join(', ') 43 | }, ...e]); 44 | }); 45 | }); 46 | }; 47 | 48 | allEvents(); 49 | return () => unsub && unsub(); 50 | }, [api.query.system]); 51 | 52 | const { feedMaxHeight = 250 } = props; 53 | 54 | return ( 55 | 56 |

Events

57 | }> 33 | Runtime Metadata 34 | 35 | 36 |
37 |                   {JSON.stringify(metadata.data, null, 2)}
38 |                 
39 |
40 |
41 | 42 | 43 | 44 |
45 | ); 46 | } 47 | 48 | export default function Metadata (props) { 49 | const { api } = useSubstrate(); 50 | return api.rpc && api.rpc.state && api.rpc.state.getMetadata 51 | ?
52 | : null; 53 | } 54 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | function Main (props) { 7 | const { api, socket } = 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 | {socket} 35 | 36 | 37 | v{nodeInfo.nodeVersion} 38 | 39 | 40 | 41 | ); 42 | } 43 | 44 | export default function NodeInfo (props) { 45 | const { api } = useSubstrate(); 46 | return api.rpc && 47 | api.rpc.system && 48 | api.rpc.system.chain && 49 | api.rpc.system.name && 50 | api.rpc.system.version 51 | ?
52 | : null; 53 | } 54 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/TemplateModule.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Form, Input, Grid, Card, Statistic } 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 { api } = useSubstrate(); 9 | const { accountPair } = props; 10 | 11 | // The transaction submission status 12 | const [status, setStatus] = useState(''); 13 | 14 | // The currently stored value 15 | const [currentValue, setCurrentValue] = useState(0); 16 | const [formValue, setFormValue] = useState(0); 17 | 18 | useEffect(() => { 19 | let unsubscribe; 20 | api.query.templateModule.something(newValue => { 21 | // The storage value is an Option 22 | // So we have to check whether it is None first 23 | // There is also unwrapOr 24 | if (newValue.isNone) { 25 | setCurrentValue(''); 26 | } else { 27 | setCurrentValue(newValue.unwrap().toNumber()); 28 | } 29 | }).then(unsub => { 30 | unsubscribe = unsub; 31 | }) 32 | .catch(console.error); 33 | 34 | return () => unsubscribe && unsubscribe(); 35 | }, [api.query.templateModule]); 36 | 37 | return ( 38 | 39 |

Template Module

40 | 41 | 42 | 46 | 47 | 48 |
49 | 50 | setFormValue(value)} 55 | /> 56 | 57 | 58 | 70 | 71 |
{status}
72 |
73 |
74 | ); 75 | } 76 | 77 | export default function TemplateModule (props) { 78 | const { api } = useSubstrate(); 79 | return api.query.templateModule && api.query.templateModule.something 80 | ?
81 | : null; 82 | } 83 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/Transfer.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Form, Input, Grid, Label, Icon } from 'semantic-ui-react'; 3 | import { TxButton } from './substrate-lib/components'; 4 | 5 | export default function Main (props) { 6 | const [status, setStatus] = useState(null); 7 | const [formState, setFormState] = useState({ addressTo: null, amount: 0 }); 8 | const { accountPair } = props; 9 | 10 | const onChange = (_, data) => 11 | setFormState(prev => ({ ...prev, [data.state]: data.value })); 12 | 13 | const { addressTo, amount } = formState; 14 | 15 | return ( 16 | 17 |

Transfer

18 |
19 | 20 | 24 | 28 | 29 | 30 | 31 | 39 | 40 | 41 | 48 | 49 | 50 | 62 | 63 |
{status}
64 |
65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/Upgrade.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Form, Input, Grid } from 'semantic-ui-react'; 3 | import { TxButton } from './substrate-lib/components'; 4 | 5 | export default function Main (props) { 6 | const [status, setStatus] = useState(''); 7 | const [proposal, setProposal] = useState({}); 8 | const { accountPair } = props; 9 | 10 | const bufferToHex = buffer => { 11 | return Array.from(new Uint8Array(buffer)) 12 | .map(b => b.toString(16).padStart(2, '0')) 13 | .join(''); 14 | }; 15 | 16 | const handleFileChosen = file => { 17 | const fileReader = new FileReader(); 18 | fileReader.onloadend = e => { 19 | const content = bufferToHex(fileReader.result); 20 | setProposal(`0x${content}`); 21 | }; 22 | 23 | fileReader.readAsArrayBuffer(file); 24 | }; 25 | 26 | return ( 27 | 28 |

Upgrade Runtime

29 |
30 | 31 | handleFileChosen(e.target.files[0])} 37 | /> 38 | 39 | 40 | 52 | 53 |
{status}
54 |
55 |
56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/__tests__/App.js: -------------------------------------------------------------------------------- 1 | // Commenting test out as it seems currently polkadot-js api is not compatible with jest 2 | // and will always cause `SyntaxError: Cannot use import statement outside a module`. 3 | // See: https://github.com/polkadot-js/api/issues/3430 4 | 5 | // import React from 'react'; 6 | // import ReactDOM from 'react-dom'; 7 | // import App from '../App'; 8 | 9 | describe('App Test Suite', () => { 10 | it('renders without crashing', () => { 11 | // const div = document.createElement('div'); 12 | // ReactDOM.render(, div); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/__tests__/mock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/config/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_NAME": "substrate-front-end-tutorial", 3 | "DEVELOPMENT_KEYRING": true, 4 | "RPC": {} 5 | } 6 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "ws://127.0.0.1:9944" 3 | } 4 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | const types = require('./types.json'); 5 | 6 | // Accepting React env vars and aggregating them into `config` object. 7 | const envVarNames = [ 8 | 'REACT_APP_PROVIDER_SOCKET', 9 | 'REACT_APP_DEVELOPMENT_KEYRING' 10 | ]; 11 | const envVars = envVarNames.reduce((mem, n) => { 12 | // Remove the `REACT_APP_` prefix 13 | if (process.env[n] !== undefined) mem[n.slice(10)] = process.env[n]; 14 | return mem; 15 | }, {}); 16 | 17 | const config = { ...configCommon, ...configEnv, ...envVars, types }; 18 | export default config; 19 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "wss://dev-node.substrate.dev" 3 | } 4 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/config/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "wss://dev-node.substrate.dev" 3 | } 4 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/config/types.json: -------------------------------------------------------------------------------- 1 | { 2 | "SpecIndex": "Vec", 3 | "RequestIdentifier": "u64", 4 | "DataVersion": "u64", 5 | "Address": "MultiAddress", 6 | "LookupSource": "MultiAddress", 7 | "FeedId": "u32", 8 | "RoundId": "u32", 9 | "Value": "u128", 10 | "FeedConfig": { 11 | "owner": "AccountId", 12 | "pending_owner": "Option", 13 | "submission_value_bounds": "(Value, Value)", 14 | "submission_count_bounds": "(u32, u32)", 15 | "payment": "Balance", 16 | "timeout": "BlockNumber", 17 | "decimals": "u8", 18 | "description": "Vec", 19 | "restart_delay": "u32", 20 | "reporting_round": "RoundId", 21 | "latest_round": "RoundId", 22 | "first_valid_round": "Option", 23 | "oracle_count": "u32", 24 | "pruning_window": "RoundId", 25 | "next_round_to_prune": "RoundId", 26 | "debt": "Balance", 27 | "max_debt": "Option" 28 | }, 29 | "FeedConfigOf": "FeedConfig", 30 | "Round": { 31 | "started_at": "BlockNumber", 32 | "answer": "Option", 33 | "updated_at": "Option", 34 | "answered_in_round": "Option" 35 | }, 36 | "RoundOf": "Round", 37 | "RoundDetails": { 38 | "submissions": "Vec", 39 | "submission_count_bounds": "(u32, u32)", 40 | "payment": "Balance", 41 | "timeout": "BlockNumber" 42 | }, 43 | "RoundDetailsOf": "RoundDetails", 44 | "OracleMeta": { 45 | "withdrawable": "Balance", 46 | "admin": "AccountId", 47 | "pending_admin": "Option" 48 | }, 49 | "OracleMetaOf": "OracleMeta", 50 | "OracleStatus": { 51 | "starting_round": "RoundId", 52 | "ending_round": "Option", 53 | "last_reported_round": "Option", 54 | "last_started_round": "Option", 55 | "latest_submission": "Option" 56 | }, 57 | "OracleStatusOf": "OracleStatus", 58 | "Requester": { 59 | "delay": "RoundId", 60 | "last_started_round": "Option" 61 | }, 62 | "RoundData": { 63 | "started_at": "BlockNumber", 64 | "answer": "Value", 65 | "updated_at": "BlockNumber", 66 | "answered_in_round": "RoundId" 67 | }, 68 | "RoundDataOf": "RoundData", 69 | "SubmissionBounds": "(u32, u32)" 70 | } -------------------------------------------------------------------------------- /substrate-node-example/front-end/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 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/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, apiState, keyring, keyringState } = useSubstrate(); 6 | if (apiState === 'READY') { window.api = api; } 7 | if (keyringState === 'READY') { window.keyring = keyring; } 8 | window.util = require('@polkadot/util'); 9 | window.utilCrypto = require('@polkadot/util-crypto'); 10 | 11 | return null; 12 | } 13 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/substrate-lib/components/index.js: -------------------------------------------------------------------------------- 1 | import { TxButton, TxGroupButton } from './TxButton'; 2 | import DeveloperConsole from './DeveloperConsole'; 3 | 4 | export { TxButton, TxGroupButton, DeveloperConsole }; 5 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/substrate-lib/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | SubstrateContextProvider, useSubstrate 3 | } from './SubstrateContext'; 4 | import utils from './utils'; 5 | 6 | export { useSubstrate, SubstrateContextProvider, utils }; 7 | -------------------------------------------------------------------------------- /substrate-node-example/front-end/src/substrate-lib/utils.js: -------------------------------------------------------------------------------- 1 | const utils = { 2 | paramConversion: { 3 | num: [ 4 | 'Compact', 5 | 'BalanceOf', 6 | 'u8', 'u16', 'u32', 'u64', 'u128', 7 | 'i8', 'i16', 'i32', 'i64', 'i128' 8 | ] 9 | } 10 | }; 11 | 12 | export default utils; 13 | -------------------------------------------------------------------------------- /substrate-node-example/mock-operator/README.md: -------------------------------------------------------------------------------- 1 | A mock of an operator setup. Allows to simply interact with a substrate based chain using the [pallet-chainlink](../pallet-chainlink) pallet. -------------------------------------------------------------------------------- /substrate-node-example/mock-operator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-operator", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@polkadot/api": "1.5.1" 8 | } 9 | } -------------------------------------------------------------------------------- /substrate-node-example/node/build.rs: -------------------------------------------------------------------------------- 1 | use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; 2 | 3 | fn main() { 4 | generate_cargo_keys(); 5 | 6 | rerun_if_git_head_changed(); 7 | } 8 | -------------------------------------------------------------------------------- /substrate-node-example/node/src/cli.rs: -------------------------------------------------------------------------------- 1 | use sc_cli::RunCmd; 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 | 13 | #[derive(Debug, StructOpt)] 14 | pub enum Subcommand { 15 | /// Key management cli utilities 16 | Key(sc_cli::KeySubcommand), 17 | /// Build a chain specification. 18 | BuildSpec(sc_cli::BuildSpecCmd), 19 | 20 | /// Validate blocks. 21 | CheckBlock(sc_cli::CheckBlockCmd), 22 | 23 | /// Export blocks. 24 | ExportBlocks(sc_cli::ExportBlocksCmd), 25 | 26 | /// Export the state of a given block into a chain spec. 27 | ExportState(sc_cli::ExportStateCmd), 28 | 29 | /// Import blocks. 30 | ImportBlocks(sc_cli::ImportBlocksCmd), 31 | 32 | /// Remove the whole chain. 33 | PurgeChain(sc_cli::PurgeChainCmd), 34 | 35 | /// Revert the chain to a previous state. 36 | Revert(sc_cli::RevertCmd), 37 | 38 | /// The custom benchmark subcommmand benchmarking runtime pallets. 39 | #[structopt(name = "benchmark", about = "Benchmark runtime pallets.")] 40 | Benchmark(frame_benchmarking_cli::BenchmarkCmd), 41 | } 42 | -------------------------------------------------------------------------------- /substrate-node-example/node/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod chain_spec; 2 | pub mod rpc; 3 | pub mod service; 4 | -------------------------------------------------------------------------------- /substrate-node-example/node/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Substrate Node Template CLI library. 2 | #![warn(missing_docs)] 3 | 4 | mod chain_spec; 5 | #[macro_use] 6 | mod service; 7 | mod cli; 8 | mod command; 9 | mod rpc; 10 | 11 | fn main() -> sc_cli::Result<()> { 12 | command::run() 13 | } 14 | -------------------------------------------------------------------------------- /substrate-node-example/node/src/rpc.rs: -------------------------------------------------------------------------------- 1 | //! A collection of node-specific RPC methods. 2 | //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer 3 | //! used by Substrate nodes. This file extends those RPC definitions with 4 | //! capabilities that are specific to this project's runtime configuration. 5 | 6 | #![warn(missing_docs)] 7 | 8 | use std::sync::Arc; 9 | 10 | use node_template_runtime::{opaque::Block, AccountId, Balance, Index}; 11 | pub use sc_rpc_api::DenyUnsafe; 12 | use sp_api::ProvideRuntimeApi; 13 | use sp_block_builder::BlockBuilder; 14 | use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; 15 | use sp_transaction_pool::TransactionPool; 16 | 17 | /// Full client dependencies. 18 | pub struct FullDeps { 19 | /// The client instance to use. 20 | pub client: Arc, 21 | /// Transaction pool instance. 22 | pub pool: Arc

, 23 | /// Whether to deny unsafe calls 24 | pub deny_unsafe: DenyUnsafe, 25 | } 26 | 27 | /// Instantiate all full RPC extensions. 28 | pub fn create_full(deps: FullDeps) -> jsonrpc_core::IoHandler 29 | where 30 | C: ProvideRuntimeApi, 31 | C: HeaderBackend + HeaderMetadata + 'static, 32 | C: Send + Sync + 'static, 33 | C::Api: substrate_frame_rpc_system::AccountNonceApi, 34 | C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, 35 | C::Api: BlockBuilder, 36 | P: TransactionPool + 'static, 37 | { 38 | use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; 39 | use substrate_frame_rpc_system::{FullSystem, SystemApi}; 40 | 41 | let mut io = jsonrpc_core::IoHandler::default(); 42 | let FullDeps { 43 | client, 44 | pool, 45 | deny_unsafe, 46 | } = deps; 47 | 48 | io.extend_with(SystemApi::to_delegate(FullSystem::new( 49 | client.clone(), 50 | pool, 51 | deny_unsafe, 52 | ))); 53 | 54 | io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new( 55 | client.clone(), 56 | ))); 57 | 58 | // Extend this RPC with a custom API by using the following syntax. 59 | // `YourRpcStruct` should have a reference to a client, which is needed 60 | // to call into the runtime. 61 | // `io.extend_with(YourRpcTrait::to_delegate(YourRpcStruct::new(ReferenceToClient, ...)));` 62 | 63 | io 64 | } 65 | -------------------------------------------------------------------------------- /substrate-node-example/pallets/template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Substrate DevHub '] 3 | description = 'FRAME pallet template for defining custom runtime logic.' 4 | edition = '2018' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-template' 8 | readme = 'README.md' 9 | repository = 'https://github.com/substrate-developer-hub/substrate-node-template/' 10 | version = '3.0.0' 11 | 12 | [package.metadata.docs.rs] 13 | targets = ['x86_64-unknown-linux-gnu'] 14 | 15 | [dependencies] 16 | serde = { version = "1.0.125", optional = true, features = ["derive"] } 17 | pallet-chainlink-feed = { path = '../../../pallet-chainlink-feed', default-features = false } 18 | codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0' } 19 | frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 20 | frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'master'} 21 | frame-benchmarking = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'master', version = "3.0.0", optional = true } 22 | 23 | [dev-dependencies] 24 | sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 25 | sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 26 | sp-runtime = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 27 | pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } 28 | 29 | [features] 30 | default = ['std'] 31 | std = [ 32 | 'serde', 33 | 'codec/std', 34 | 'frame-support/std', 35 | 'frame-system/std', 36 | 'frame-benchmarking/std', 37 | 'pallet-chainlink-feed/std' 38 | ] 39 | runtime-benchmarks = ['frame-benchmarking'] 40 | # Note: frame-support `try-runtime` feature is released after v3. 41 | # Uncomment the following line when `frame-support` version > `3.0.0`. 42 | # try-runtime = ['frame-support/try-runtime'] 43 | -------------------------------------------------------------------------------- /substrate-node-example/pallets/template/src/benchmarking.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarking setup for pallet-template 2 | 3 | use super::*; 4 | 5 | #[allow(unused)] 6 | use crate::Pallet as Template; 7 | use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; 8 | use frame_system::RawOrigin; 9 | 10 | benchmarks! { 11 | do_something { 12 | let s in 0 .. 100; 13 | let caller: T::AccountId = whitelisted_caller(); 14 | }: _(RawOrigin::Signed(caller), s) 15 | verify { 16 | assert_eq!(Something::::get(), Some(s)); 17 | } 18 | } 19 | 20 | impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test,); 21 | -------------------------------------------------------------------------------- /substrate-node-example/pallets/template/src/mock.rs: -------------------------------------------------------------------------------- 1 | use crate as pallet_template; 2 | use frame_support::{parameter_types, PalletId}; 3 | use frame_system as system; 4 | use sp_core::H256; 5 | use sp_runtime::{ 6 | testing::Header, 7 | traits::{BlakeTwo256, IdentityLookup}, 8 | }; 9 | 10 | type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; 11 | type Block = frame_system::mocking::MockBlock; 12 | 13 | // Configure a mock runtime to test the pallet. 14 | frame_support::construct_runtime!( 15 | pub enum Test where 16 | Block = Block, 17 | NodeBlock = Block, 18 | UncheckedExtrinsic = UncheckedExtrinsic, 19 | { 20 | System: frame_system::{Pallet, Call, Config, Storage, Event}, 21 | Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, 22 | ChainlinkFeed: pallet_chainlink_feed::{Pallet, Call, Storage, Event}, 23 | TemplateModule: pallet_template::{Pallet, Call, Storage, Event}, 24 | } 25 | ); 26 | 27 | parameter_types! { 28 | pub const BlockHashCount: u64 = 250; 29 | pub const SS58Prefix: u8 = 42; 30 | } 31 | 32 | impl system::Config for Test { 33 | type BaseCallFilter = (); 34 | type BlockWeights = (); 35 | type BlockLength = (); 36 | type DbWeight = (); 37 | type Origin = Origin; 38 | type Call = Call; 39 | type Index = u64; 40 | type BlockNumber = u64; 41 | type Hash = H256; 42 | type Hashing = BlakeTwo256; 43 | type AccountId = u64; 44 | type Lookup = IdentityLookup; 45 | type Header = Header; 46 | type Event = Event; 47 | type BlockHashCount = BlockHashCount; 48 | type Version = (); 49 | type PalletInfo = PalletInfo; 50 | type AccountData = pallet_balances::AccountData; 51 | type OnNewAccount = (); 52 | type OnKilledAccount = (); 53 | type SystemWeightInfo = (); 54 | type SS58Prefix = SS58Prefix; 55 | type OnSetCode = (); 56 | } 57 | 58 | parameter_types! { 59 | pub const ExistentialDeposit: u64 = 1; 60 | } 61 | 62 | type Balance = u64; 63 | 64 | impl pallet_balances::Config for Test { 65 | type MaxLocks = (); 66 | type Balance = Balance; 67 | type Event = Event; 68 | type DustRemoval = (); 69 | type ExistentialDeposit = ExistentialDeposit; 70 | type AccountStore = System; 71 | type WeightInfo = (); 72 | type MaxReserves = (); 73 | type ReserveIdentifier = [u8; 8]; 74 | } 75 | 76 | pub(crate) const MIN_RESERVE: u64 = 100; 77 | 78 | parameter_types! { 79 | pub const FeedPalletId: PalletId = PalletId(*b"linkfeed"); 80 | pub const MinimumReserve: u64 = MIN_RESERVE; 81 | pub const StringLimit: u32 = 15; 82 | pub const OracleLimit: u32 = 10; 83 | pub const FeedLimit: u16 = 10; 84 | pub const PruningWindow: u32 = 3; 85 | } 86 | 87 | type FeedId = u16; 88 | type Value = u64; 89 | 90 | impl pallet_chainlink_feed::Config for Test { 91 | type Event = Event; 92 | type FeedId = FeedId; 93 | type Value = Value; 94 | type Currency = Balances; 95 | type PalletId = FeedPalletId; 96 | type MinimumReserve = MinimumReserve; 97 | type StringLimit = StringLimit; 98 | type OracleCountLimit = OracleLimit; 99 | type FeedLimit = FeedLimit; 100 | type WeightInfo = (); 101 | type OnAnswerHandler = (); 102 | } 103 | 104 | impl pallet_template::Config for Test { 105 | type Event = Event; 106 | type Oracle = ChainlinkFeed; 107 | } 108 | 109 | // Build genesis storage according to the mock runtime. 110 | pub fn new_test_ext() -> sp_io::TestExternalities { 111 | system::GenesisConfig::default() 112 | .build_storage::() 113 | .unwrap() 114 | .into() 115 | } 116 | -------------------------------------------------------------------------------- /substrate-node-example/pallets/template/src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{mock::*, Error}; 2 | use frame_support::{assert_noop, assert_ok}; 3 | 4 | #[test] 5 | fn it_works_for_default_value() { 6 | new_test_ext().execute_with(|| { 7 | // Dispatch a signed extrinsic. 8 | assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); 9 | // Read pallet storage and assert an expected result. 10 | assert_eq!(TemplateModule::something(), Some(42)); 11 | }); 12 | } 13 | 14 | #[test] 15 | fn correct_error_for_none_value() { 16 | new_test_ext().execute_with(|| { 17 | // Ensure the expected error is thrown when no value is present. 18 | assert_noop!( 19 | TemplateModule::cause_error(Origin::signed(1)), 20 | Error::::NoneValue 21 | ); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /substrate-node-example/runtime/build.rs: -------------------------------------------------------------------------------- 1 | use substrate_wasm_builder::WasmBuilder; 2 | 3 | fn main() { 4 | WasmBuilder::new() 5 | .with_current_project() 6 | .export_heap_base() 7 | .import_memory() 8 | .build() 9 | } 10 | -------------------------------------------------------------------------------- /substrate-node-example/runtime/src/example.rs: -------------------------------------------------------------------------------- 1 | use codec::{Decode, Encode}; 2 | use frame_support::sp_runtime::traits::UniqueSaturatedFrom; 3 | use frame_support::traits::Currency; 4 | use frame_support::{decl_module, decl_storage, dispatch::DispatchResult}; 5 | use frame_system::ensure_root; 6 | use pallet_chainlink::{CallbackWithParameter, Config as ChainlinkTrait, Event}; 7 | use sp_std::prelude::*; 8 | 9 | type BalanceOf = <::Currency as Currency< 10 | ::AccountId, 11 | >>::Balance; 12 | 13 | pub trait Config: pallet_chainlink::Config + ChainlinkTrait { 14 | type Event: From> + Into<::Event>; 15 | type Callback: From> + Into<::Callback>; 16 | } 17 | 18 | decl_storage! { 19 | trait Store for Module as ExampleStorage { 20 | pub Result: i128; 21 | } 22 | } 23 | 24 | decl_module! { 25 | pub struct Module for enum Call where origin: T::Origin { 26 | fn deposit_event() = default; 27 | 28 | #[weight = 0] 29 | pub fn send_request(origin, operator: T::AccountId, specid: Vec) -> DispatchResult { 30 | let parameters = ("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD", "path", "RAW.ETH.USD.PRICE", "times", "100000000"); 31 | let call: ::Callback = Call::callback(vec![]).into(); 32 | 33 | let fee = BalanceOf::::unique_saturated_from(100u32); 34 | >::initiate_request(origin, operator, specid, 0, parameters.encode(), fee, call.into())?; 35 | 36 | Ok(()) 37 | } 38 | 39 | #[weight = 0] 40 | pub fn callback(origin, result: Vec) -> DispatchResult { 41 | ensure_root(origin)?; 42 | 43 | // The result is expected to be a SCALE encoded `i128` 44 | let r : i128 = i128::decode(&mut &result[..]).map_err(|_| Error::::DecodingFailed)?; 45 | ::put(r); 46 | 47 | Ok(()) 48 | } 49 | } 50 | } 51 | 52 | frame_support::decl_error! { 53 | pub enum Error for Module { 54 | DecodingFailed 55 | } 56 | } 57 | 58 | impl CallbackWithParameter for Call { 59 | fn with_result(&self, result: Vec) -> Option { 60 | match *self { 61 | Call::callback(_) => Some(Call::callback(result)), 62 | _ => None, 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /substrate-node-example/runtime/src/weights/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pallet_chainlink_feed; 2 | -------------------------------------------------------------------------------- /substrate-node-example/scripts/docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Start Substrate node template ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | 9 | docker-compose down --remove-orphans 10 | docker-compose run --rm --service-ports dev $@ -------------------------------------------------------------------------------- /substrate-node-example/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 | rustup target add wasm32-unknown-unknown --toolchain nightly-2020-10-05 14 | -------------------------------------------------------------------------------- /substrate-node-example/specs/README.md: -------------------------------------------------------------------------------- 1 | This folder contains different chains-specs you can use to specify the example chain's specifications 2 | 3 | To run the example chain with a custom chainspec: 4 | 5 | ```shell 6 | cargo build --release 7 | ./target/release/node-template --chain=./specs/chainlink-feed.json --tmp --alice 8 | ``` 9 | 10 | You can check that the feed is included in the chain state: 11 | - use the polkadot UI -> Developer -> Chain State -> chainlinkFeed -> Feeds(0) 12 | 13 | If it shows `` you need to register the custom [types](../types.json) by copying the json types into Settings -> Developer in the UI 14 | 15 | If the chain blocks access from the UI (`Blocked connection to WebSockets server from untrusted origin`) add the `--rpc-cors all` option 16 | 17 | 18 | Read more about substrate's [Chain Specification](https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec) and [creating private networks](https://substrate.dev/docs/en/tutorials/start-a-private-network) 19 | 20 | 21 | * [chainlink.json](chainlink.json) is a spec with multiple registered feed creators (Alice, Bob, Charlie, Dave, Eve, Ferdie as well as their stash accounts). pallet admin is Alice 22 | * [chainlink-feed.json](chainlink-feed.json) is a spec with the same feed creators and a feed owned by Alice, several oracles (Bob, Charlie, Dave, Eve) and Ferdie as their admin. 23 | To add more feeds, add another feed in the `feeds` array. 24 | * [chainlink-no-admin-funds.json](chainlink-no-admin-funds.json) is a spec the same feed creators but the admin is Ferdie which has no endowment. 25 | 26 | The `--chain` command of the node is modified to accept also feed config json files like [feedconfig.json](feedconfig.json) directly. In this case the same config as `--dev` is used but with feed config of the given json file. 27 | In addtion the `--chain` option also accepts a feed config string directly. 28 | 29 | ```shell 30 | ./target/release/node-template --chain=./specs/feedconfig.json --tmp --alice 31 | ``` 32 | 33 | The genesis config of the `chainlink-feed` pallet can be created like: 34 | 35 | ```rust 36 | chainlink_feed: ChainlinkFeedConfig { 37 | pallet_admin: Some(root_key), 38 | // accounts that are allowed to create feeds, must include the owners of the `feeds` 39 | feed_creators: vec![get_account_id_from_seed::("Alice")], 40 | feeds: vec![node_template_runtime::FeedBuilder::new() 41 | // owner of this feed 42 | .owner(get_account_id_from_seed::("Alice")) 43 | .decimals(8) 44 | // payment of oracle rounds 45 | .payment(1_000) 46 | // round initiation delay 47 | .restart_delay(0) 48 | // timeout of blocks 49 | .timeout(10) 50 | .description(b"LINK".to_vec()) 51 | .value_bounds(1, 1_000) 52 | .min_submissions(2) 53 | // make Bob, Charlie, Dave and Eve oracles and Ferdie their admin 54 | .oracles(vec![ 55 | ( 56 | get_account_id_from_seed::("Bob"), 57 | get_account_id_from_seed::("Ferdie"), 58 | ), 59 | ( 60 | get_account_id_from_seed::("Charlie"), 61 | get_account_id_from_seed::("Ferdie"), 62 | ) 63 | ])] 64 | }, 65 | ``` -------------------------------------------------------------------------------- /substrate-node-example/specs/feedconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "palletAdmin": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 3 | "feedCreators": [ 4 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 5 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 6 | "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 7 | "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", 8 | "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", 9 | "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", 10 | "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", 11 | "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", 12 | "5Ck5SLSHYac6WFt5UZRSsdJjwmpSZq85fd5TRNAdZQVzEAPT", 13 | "5HKPmK9GYtE1PSLsS1qiYU9xQ9Si1NcEhdeCq9sw5bqu4ns8", 14 | "5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n", 15 | "5CRmqmsiNFExV6VbdmPJViVxrWmkaXXvBrSX8oqBT8R9vmWk" 16 | ], 17 | "feeds": [ 18 | { 19 | "owner": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 20 | "payment": 1000, 21 | "timeout": 10, 22 | "value_bounds": [ 23 | 1, 24 | 1000 25 | ], 26 | "min_submissions": 2, 27 | "description": [ 28 | 76, 29 | 73, 30 | 78, 31 | 75 32 | ], 33 | "decimals": 8, 34 | "restart_delay": 0, 35 | "oracles": [ 36 | [ 37 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 38 | "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" 39 | ], 40 | [ 41 | "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 42 | "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" 43 | ], 44 | [ 45 | "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", 46 | "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" 47 | ], 48 | [ 49 | "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", 50 | "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" 51 | ] 52 | ], 53 | "pruning_window": null, 54 | "max_debt": null 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /substrate-node-example/types.json: -------------------------------------------------------------------------------- 1 | { 2 | "SpecIndex": "Vec", 3 | "RequestIdentifier": "u64", 4 | "DataVersion": "u64", 5 | "Address": "MultiAddress", 6 | "LookupSource": "MultiAddress", 7 | "FeedId": "u32", 8 | "RoundId": "u32", 9 | "Value": "u128", 10 | "FeedConfig": { 11 | "owner": "AccountId", 12 | "pending_owner": "Option", 13 | "submission_value_bounds": "(Value, Value)", 14 | "submission_count_bounds": "(u32, u32)", 15 | "payment": "Balance", 16 | "timeout": "BlockNumber", 17 | "decimals": "u8", 18 | "description": "Vec", 19 | "restart_delay": "u32", 20 | "reporting_round": "RoundId", 21 | "latest_round": "RoundId", 22 | "first_valid_round": "Option", 23 | "oracle_count": "u32", 24 | "pruning_window": "u32", 25 | "next_round_to_prune": "RoundId", 26 | "debt": "Balance", 27 | "max_debt": "Option" 28 | }, 29 | "FeedConfigOf": "FeedConfig", 30 | "Round": { 31 | "started_at": "BlockNumber", 32 | "answer": "Option", 33 | "updated_at": "Option", 34 | "answered_in_round": "Option" 35 | }, 36 | "RoundOf": "Round", 37 | "RoundDetails": { 38 | "submissions": "Vec", 39 | "submission_count_bounds": "(u32, u32)", 40 | "payment": "Balance", 41 | "timeout": "BlockNumber" 42 | }, 43 | "RoundDetailsOf": "RoundDetails", 44 | "OracleMeta": { 45 | "withdrawable": "Balance", 46 | "admin": "AccountId", 47 | "pending_admin": "Option" 48 | }, 49 | "OracleMetaOf": "OracleMeta", 50 | "OracleStatus": { 51 | "starting_round": "RoundId", 52 | "ending_round": "Option", 53 | "last_reported_round": "Option", 54 | "last_started_round": "Option", 55 | "latest_submission": "Option" 56 | }, 57 | "OracleStatusOf": "OracleStatus", 58 | "Requester": { 59 | "delay": "RoundId", 60 | "last_started_round": "Option" 61 | }, 62 | "RoundData": { 63 | "started_at": "BlockNumber", 64 | "answer": "Value", 65 | "updated_at": "BlockNumber", 66 | "answered_in_round": "RoundId" 67 | }, 68 | "RoundDataOf": "RoundData", 69 | "SubmissionBounds": "(u32, u32)" 70 | } --------------------------------------------------------------------------------