From 806d57638367254af992de87aef88a5d765ce3b3 Mon Sep 17 00:00:00 2001 From: PatrickAlphaC Date: Sun, 13 Dec 2020 22:53:37 -0500 Subject: [PATCH] finalized --- README.md | 117 +- brownie-config.yaml | 27 +- build/contracts/PriceFeed.json | 1089 +++++++++-------- build/deployments/map.json | 81 +- build/tests.json | 28 +- contracts/APIConsumer.sol | 78 ++ contracts/PriceFeed.sol | 13 +- contracts/VRFConsumer.sol | 45 + interfaces/LinkTokenInterface.sol | 16 + .../deploy_api_consumer.py | 12 + .../fund_chainlink_api.py | 12 + scripts/chainlink_api_scripts/read_data.py | 8 + scripts/chainlink_api_scripts/request_api.py | 10 + scripts/deploy_price_consumer_v3.py | 7 - .../deploy_price_consumer_v3.py | 16 + scripts/price_feed_scripts/read_price_feed.py | 8 + scripts/vrf_scripts/deploy_vrf.py | 14 + scripts/vrf_scripts/fund_vrf.py | 11 + scripts/vrf_scripts/read_random_number.py | 9 + scripts/vrf_scripts/request_randomness.py | 12 + tests/conftest.py | 23 + .../test_mainnet_fork_price_feed.py | 25 + tests/staging/test_kovan_api.py | 39 + tests/staging/test_kovan_vrf.py | 38 + tests/test_price_feed.py | 8 - 25 files changed, 1155 insertions(+), 591 deletions(-) create mode 100644 contracts/APIConsumer.sol create mode 100644 contracts/VRFConsumer.sol create mode 100644 interfaces/LinkTokenInterface.sol create mode 100644 scripts/chainlink_api_scripts/deploy_api_consumer.py create mode 100644 scripts/chainlink_api_scripts/fund_chainlink_api.py create mode 100644 scripts/chainlink_api_scripts/read_data.py create mode 100644 scripts/chainlink_api_scripts/request_api.py delete mode 100644 scripts/deploy_price_consumer_v3.py create mode 100644 scripts/price_feed_scripts/deploy_price_consumer_v3.py create mode 100644 scripts/price_feed_scripts/read_price_feed.py create mode 100644 scripts/vrf_scripts/deploy_vrf.py create mode 100644 scripts/vrf_scripts/fund_vrf.py create mode 100644 scripts/vrf_scripts/read_random_number.py create mode 100644 scripts/vrf_scripts/request_randomness.py create mode 100644 tests/conftest.py create mode 100644 tests/mainnet-fork/test_mainnet_fork_price_feed.py create mode 100644 tests/staging/test_kovan_api.py create mode 100644 tests/staging/test_kovan_vrf.py delete mode 100644 tests/test_price_feed.py diff --git a/README.md b/README.md index fe513a5..65a8f0f 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,126 @@ -# token-mix +# chainlink-mix -A bare-bones implementation of the Ethereum [ERC-20 standard](https://eips.ethereum.org/EIPS/eip-20), written in [Solidity](https://github.com/ethereum/solidity). +This is a repo to work with and use Chainlink smart contracts in a python environment. If you're brand new to Chainlink, check out the beginer walkthroughs in remix to [learn the basics.](https://docs.chain.link/docs/beginners-tutorial) -For [Vyper](https://github.com/vyperlang/vyper), check out [`vyper-token-mix`](https://github.com/brownie-mix/vyper-token-mix). +You can also check out the more advanced Chainlink tutorials there as well. ## Installation -1. [Install Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html), if you haven't already. +1. [Install Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html), if you haven't already. Here is a simple way to install brownie. -2. Download the mix. +```bash +pip install eth-brownie +``` - ```bash - brownie bake token - ``` +2. Download the mix. #TODO -## Basic Use +Until the mix is uploaded, you can just do the following: -This mix provides a [simple template](contracts/Token.sol) upon which you can build your own token, as well as unit tests providing 100% coverage for core ERC20 functionality. +```bash +git clone https://github.com/PatrickAlphaC/chainlink-mix +cd chainlink-mix +``` -To interact with a deployed contract in a local environment, start by opening the console: +Once it becomes a mix, it will look like: ```bash -brownie console +brownie bake chainlink-mix +cd chainlink-mix ``` -Next, deploy a test token: +3. Set your `WEB3_INFURA_PROJECT_ID`. You can get this by getting a free trial of [Infura](https://infura.io/). At the moment, it does need to be infura. + +## Chainlink Price Feeds + +This mix provides a simple template for working with Chainlink Smart Contracts. The easiest way to start is to fork the mainnet chain to a local ganache chain. This will allow you to deploy local smart contracts to interact with the [Chainlink Price Feeds](https://docs.chain.link/docs/get-the-latest-price). -```python ->>> token = Token.deploy("Test Token", "TST", 18, 1e21, {'from': accounts[0]}) +### Running Scripts -Transaction sent: 0x4a61edfaaa8ba55573603abd35403cf41291eca443c983f85de06e0b119da377 - Gas price: 0.0 gwei Gas limit: 12000000 - Token.constructor confirmed - Block: 1 Gas used: 521513 (4.35%) - Token deployed at: 0xd495633B90a237de510B4375c442C0469D3C161C +This will deploy a smart contract to kovan and then read you the latest price via [Chainlink Price Feeds](https://docs.chain.link/docs/get-the-latest-price). +``` +brownie run scripts/price_feed_scripts/deploy_price_consumer_v3.py --network kovan +brownie run scripts/price_feed_scripts/read_price_feed.py --network kovan +``` + +Otherwise, you can fork mainnet and use that in a local ganache development environment. +```bash +brownie console --network mainnet-fork +>>> price_feeds = PriceFeed.deploy('0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419', {'from': accounts[0]}) +. +. +>>> latest_price = price_feeds.getLatestPrice() +>>> latest_price +59169208540 ``` -You now have a token contract deployed, with a balance of `1e21` assigned to `accounts[0]`: +## Chainlink VRF -```python ->>> token - +This will deploy a smart contract to kovan and get a Random number via [Chainlink VRF](https://docs.chain.link/docs/get-a-random-number). +``` +brownie run scripts/price_feed_scripts/deploy_vrf.py --network kovan +brownie run scripts/price_feed_scripts/fund_vrf.py --network kovan +brownie run scripts/price_feed_scripts/request_randomness.py --network kovan +brownie run scripts/price_feed_scripts/read_random_number.py --network kovan +``` ->>> token.balanceOf(accounts[0]) -1000000000000000000000 +## Chainlink API Call ->>> token.transfer(accounts[1], 1e18, {'from': accounts[0]}) -Transaction sent: 0xb94b219148501a269020158320d543946a4e7b9fac294b17164252a13dce9534 - Gas price: 0.0 gwei Gas limit: 12000000 - Token.transfer confirmed - Block: 2 Gas used: 51668 (0.43%) - +This will deploy a smart contract to kovan and then make an API call via [Chainlink API Call](https://docs.chain.link/docs/make-a-http-get-request). +``` +brownie run scripts/price_feed_scripts/deploy_api_consumer.py --network kovan +brownie run scripts/price_feed_scripts/fund_chainlink_api.py --network kovan +brownie run scripts/price_feed_scripts/request_api.py --network kovan +brownie run scripts/price_feed_scripts/read_api.py --network kovan ``` ## Testing -To run the tests: +To run basic tests from `mainnet-fork` network -```bash +``` brownie test ``` -The unit tests included in this mix are very generic and should work with any ERC20 compliant smart contract. To use them in your own project, all you must do is modify the deployment logic in the [`tests/conftest.py::token`](tests/conftest.py) fixture. +There are 3 types of tests you can run: + +1. Development tests + 1. These test using a local ganache chain. They are unit tests and do not interact with Chainlink nodes. + 2. Right now this is blank +2. Mainnet-fork tests + 1. These run on a mainnet forked local ganache chain. They are used to interact with pricefeed contracts deployed to mainnet. +3. Testnet/Staging tests + 1. These are the pre-production tests. They are used to test a specific testnet. + 2. Right now we have them hard coded to kovan. + + +### To test development (Currently blank) +```bash +brownie test --network development +``` +### To test mainnet-fork +This will test Chainlink API Calls and Chainlink VRF +```bash +brownie test --network mainnet-fork +``` +### To test staging/testnet +This will test Chainlink Price Feeds +```bash +brownie test --network kovan +``` ## Resources To get started with Brownie: +* [Chainlink Documentation](https://docs.chain.link/docs) +* Check out the [Chainlink documentation](https://docs.chain.link/docs) to get started from any level of smart contract engineering. * Check out the other [Brownie mixes](https://github.com/brownie-mix/) that can be used as a starting point for your own contracts. They also provide example code to help you get started. * ["Getting Started with Brownie"](https://medium.com/@iamdefinitelyahuman/getting-started-with-brownie-part-1-9b2181f4cb99) is a good tutorial to help you familiarize yourself with Brownie. * For more in-depth information, read the [Brownie documentation](https://eth-brownie.readthedocs.io/en/stable/). -Any questions? Join our [Gitter](https://gitter.im/eth-brownie/community) channel to chat and share with others in the community. +Any questions? Join our [Discord](https://discord.gg/2YHSAey) ## License diff --git a/brownie-config.yaml b/brownie-config.yaml index ca793ef..0b4c924 100644 --- a/brownie-config.yaml +++ b/brownie-config.yaml @@ -3,14 +3,31 @@ reports: exclude_contracts: - SafeMath +dependencies: + - alphachainio/chainlink-contracts@1.1.0 compiler: solc: remappings: - - '@chainlink-contracts=alphachainio/chainlink-contracts@1.0.0' + - '@chainlink=alphachainio/chainlink-contracts@1.1.0' +# automatically fetch contract sources from Etherscan +autofetch_sources: True # set a custom mnemonic for the development network networks: - default: kovan + default: mainnet-fork kovan: - cmd_settings: - mnemonic: ${MNEMONIC} - host: ${KOVAN_RPC_URL} + vrf_coordinator: '0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9' + link_token: '0xa36085F69e2889c224210F603D836748e7dC0088' + keyhash: '0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4' + fee: 100000000000000000 + oracle: '0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e' + jobId: '29fa9aa13bf1468788b7cc4a500a45b8' + rinkeby: + vrf_coordinator: '0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B' + link_token: '0x01be23585060835e02b77ef475b0cc51aa1e0709' + keyhash: '0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311' + fee: 100000000000000000 + oracle: '0x7AFe1118Ea78C1eae84ca8feE5C65Bc76CcF879e' + jobId: '6d1bfe27e7034b1d87b5270556b17277' +wallets: + from_key: PRIVATE_KEY + # could also do from_mnemonic, and you'd have to change the accounts.add to accounts.from_mnemonic diff --git a/build/contracts/PriceFeed.json b/build/contracts/PriceFeed.json index 04527d0..bc5be75 100644 --- a/build/contracts/PriceFeed.json +++ b/build/contracts/PriceFeed.json @@ -1,7 +1,13 @@ { "abi": [ { - "inputs": [], + "inputs": [ + { + "internalType": "address", + "name": "AggregatorAddress", + "type": "address" + } + ], "stateMutability": "nonpayable", "type": "constructor" }, @@ -20,38 +26,38 @@ } ], "allSourcePaths": { - "1": "contracts/PriceFeed.sol" + "12": "contracts/PriceFeed.sol" }, "ast": { "absolutePath": "contracts/PriceFeed.sol", "exportedSymbols": { "PriceFeed": [ - 39 + 152 ] }, - "id": 40, + "id": 153, "license": "MIT", "nodeType": "SourceUnit", "nodes": [ { - "id": 1, + "id": 112, "literals": [ "solidity", "^", "0.6", - ".7" + ".6" ], "nodeType": "PragmaDirective", - "src": "34:23:1" + "src": "34:23:12" }, { - "absolutePath": "/Users/patrick/.brownie/packages/alphachainio/chainlink-contracts@1.0.0/src/v0.6/interfaces/AggregatorV3Interface.sol", - "file": "@chainlink-contracts/src/v0.6/interfaces/AggregatorV3Interface.sol", - "id": 2, + "absolutePath": "/Users/patrick/.brownie/packages/alphachainio/chainlink-contracts@1.1.0/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol", + "file": "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol", + "id": 113, "nodeType": "ImportDirective", - "scope": 40, - "sourceUnit": 86, - "src": "59:76:1", + "scope": 153, + "sourceUnit": 661, + "src": "59:76:12", "symbolAliases": [], "unitAlias": "" }, @@ -62,37 +68,37 @@ "contractKind": "contract", "documentation": null, "fullyImplemented": true, - "id": 39, + "id": 152, "linearizedBaseContracts": [ - 39 + 152 ], "name": "PriceFeed", "nodeType": "ContractDefinition", "nodes": [ { "constant": false, - "id": 4, + "id": 115, "mutability": "mutable", "name": "priceFeed", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 39, - "src": "163:40:1", + "scope": 152, + "src": "163:40:12", "stateVariable": true, "storageLocation": "default", "typeDescriptions": { - "typeIdentifier": "t_contract$_AggregatorV3Interface_$85", + "typeIdentifier": "t_contract$_AggregatorV3Interface_$660", "typeString": "contract AggregatorV3Interface" }, "typeName": { "contractScope": null, - "id": 3, + "id": 114, "name": "AggregatorV3Interface", "nodeType": "UserDefinedTypeName", - "referencedDeclaration": 85, - "src": "163:21:1", + "referencedDeclaration": 660, + "src": "163:21:12", "typeDescriptions": { - "typeIdentifier": "t_contract$_AggregatorV3Interface_$85", + "typeIdentifier": "t_contract$_AggregatorV3Interface_$660", "typeString": "contract AggregatorV3Interface" } }, @@ -101,28 +107,28 @@ }, { "body": { - "id": 14, + "id": 127, "nodeType": "Block", - "src": "355:94:1", + "src": "506:69:12", "statements": [ { "expression": { "argumentTypes": null, - "id": 12, + "id": 125, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "argumentTypes": null, - "id": 8, + "id": 121, "name": "priceFeed", "nodeType": "Identifier", "overloadedDeclarations": [], - "referencedDeclaration": 4, - "src": "365:9:1", + "referencedDeclaration": 115, + "src": "516:9:12", "typeDescriptions": { - "typeIdentifier": "t_contract$_AggregatorV3Interface_$85", + "typeIdentifier": "t_contract$_AggregatorV3Interface_$660", "typeString": "contract AggregatorV3Interface" } }, @@ -133,75 +139,70 @@ "arguments": [ { "argumentTypes": null, - "hexValue": "307839333236424641303241444432333636623330626163423132353236304166363431303331333331", - "id": 10, - "isConstant": false, - "isLValue": false, - "isPure": true, - "kind": "number", - "lValueRequested": false, - "nodeType": "Literal", - "src": "399:42:1", - "subdenomination": null, + "id": 123, + "name": "AggregatorAddress", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 118, + "src": "550:17:12", "typeDescriptions": { - "typeIdentifier": "t_address_payable", - "typeString": "address payable" - }, - "value": "0x9326BFA02ADD2366b30bacB125260Af641031331" + "typeIdentifier": "t_address", + "typeString": "address" + } } ], "expression": { "argumentTypes": [ { - "typeIdentifier": "t_address_payable", - "typeString": "address payable" + "typeIdentifier": "t_address", + "typeString": "address" } ], - "id": 9, + "id": 122, "name": "AggregatorV3Interface", "nodeType": "Identifier", "overloadedDeclarations": [], - "referencedDeclaration": 85, - "src": "377:21:1", + "referencedDeclaration": 660, + "src": "528:21:12", "typeDescriptions": { - "typeIdentifier": "t_type$_t_contract$_AggregatorV3Interface_$85_$", + "typeIdentifier": "t_type$_t_contract$_AggregatorV3Interface_$660_$", "typeString": "type(contract AggregatorV3Interface)" } }, - "id": 11, + "id": 124, "isConstant": false, "isLValue": false, - "isPure": true, + "isPure": false, "kind": "typeConversion", "lValueRequested": false, "names": [], "nodeType": "FunctionCall", - "src": "377:65:1", + "src": "528:40:12", "tryCall": false, "typeDescriptions": { - "typeIdentifier": "t_contract$_AggregatorV3Interface_$85", + "typeIdentifier": "t_contract$_AggregatorV3Interface_$660", "typeString": "contract AggregatorV3Interface" } }, - "src": "365:77:1", + "src": "516:52:12", "typeDescriptions": { - "typeIdentifier": "t_contract$_AggregatorV3Interface_$85", + "typeIdentifier": "t_contract$_AggregatorV3Interface_$660", "typeString": "contract AggregatorV3Interface" } }, - "id": 13, + "id": 126, "nodeType": "ExpressionStatement", - "src": "365:77:1" + "src": "516:52:12" } ] }, "documentation": { - "id": 5, + "id": 116, "nodeType": "StructuredDocumentation", - "src": "210:119:1", - "text": " Network: Kovan\n Aggregator: ETH/USD\n Address: 0x9326BFA02ADD2366b30bacB125260Af641031331" + "src": "334:121:12", + "text": " Network: Mainnet\n Aggregator: ETH/USD\n Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419" }, - "id": 15, + "id": 128, "implemented": true, "kind": "constructor", "modifiers": [], @@ -209,47 +210,77 @@ "nodeType": "FunctionDefinition", "overrides": null, "parameters": { - "id": 6, + "id": 119, "nodeType": "ParameterList", - "parameters": [], - "src": "345:2:1" + "parameters": [ + { + "constant": false, + "id": 118, + "mutability": "mutable", + "name": "AggregatorAddress", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 128, + "src": "472:25:12", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 117, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "472:7:12", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "value": null, + "visibility": "internal" + } + ], + "src": "471:27:12" }, "returnParameters": { - "id": 7, + "id": 120, "nodeType": "ParameterList", "parameters": [], - "src": "355:0:1" + "src": "506:0:12" }, - "scope": 39, - "src": "334:115:1", + "scope": 152, + "src": "460:115:12", "stateMutability": "nonpayable", "virtual": false, "visibility": "public" }, { "body": { - "id": 37, + "id": 150, "nodeType": "Block", - "src": "555:223:1", + "src": "681:223:12", "statements": [ { "assignments": [ - 22, - 24, - 26, - 28, - 30 + 135, + 137, + 139, + 141, + 143 ], "declarations": [ { "constant": false, - "id": 22, + "id": 135, "mutability": "mutable", "name": "roundID", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 37, - "src": "579:14:1", + "scope": 150, + "src": "705:14:12", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -257,10 +288,10 @@ "typeString": "uint80" }, "typeName": { - "id": 21, + "id": 134, "name": "uint80", "nodeType": "ElementaryTypeName", - "src": "579:6:1", + "src": "705:6:12", "typeDescriptions": { "typeIdentifier": "t_uint80", "typeString": "uint80" @@ -271,13 +302,13 @@ }, { "constant": false, - "id": 24, + "id": 137, "mutability": "mutable", "name": "price", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 37, - "src": "608:9:1", + "scope": 150, + "src": "734:9:12", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -285,10 +316,10 @@ "typeString": "int256" }, "typeName": { - "id": 23, + "id": 136, "name": "int", "nodeType": "ElementaryTypeName", - "src": "608:3:1", + "src": "734:3:12", "typeDescriptions": { "typeIdentifier": "t_int256", "typeString": "int256" @@ -299,13 +330,13 @@ }, { "constant": false, - "id": 26, + "id": 139, "mutability": "mutable", "name": "startedAt", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 37, - "src": "631:14:1", + "scope": 150, + "src": "757:14:12", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -313,10 +344,10 @@ "typeString": "uint256" }, "typeName": { - "id": 25, + "id": 138, "name": "uint", "nodeType": "ElementaryTypeName", - "src": "631:4:1", + "src": "757:4:12", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" @@ -327,13 +358,13 @@ }, { "constant": false, - "id": 28, + "id": 141, "mutability": "mutable", "name": "timeStamp", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 37, - "src": "659:14:1", + "scope": 150, + "src": "785:14:12", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -341,10 +372,10 @@ "typeString": "uint256" }, "typeName": { - "id": 27, + "id": 140, "name": "uint", "nodeType": "ElementaryTypeName", - "src": "659:4:1", + "src": "785:4:12", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" @@ -355,13 +386,13 @@ }, { "constant": false, - "id": 30, + "id": 143, "mutability": "mutable", "name": "answeredInRound", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 37, - "src": "687:22:1", + "scope": 150, + "src": "813:22:12", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -369,10 +400,10 @@ "typeString": "uint80" }, "typeName": { - "id": 29, + "id": 142, "name": "uint80", "nodeType": "ElementaryTypeName", - "src": "687:6:1", + "src": "813:6:12", "typeDescriptions": { "typeIdentifier": "t_uint80", "typeString": "uint80" @@ -382,7 +413,7 @@ "visibility": "internal" } ], - "id": 34, + "id": 147, "initialValue": { "argumentTypes": null, "arguments": [], @@ -390,32 +421,32 @@ "argumentTypes": [], "expression": { "argumentTypes": null, - "id": 31, + "id": 144, "name": "priceFeed", "nodeType": "Identifier", "overloadedDeclarations": [], - "referencedDeclaration": 4, - "src": "722:9:1", + "referencedDeclaration": 115, + "src": "848:9:12", "typeDescriptions": { - "typeIdentifier": "t_contract$_AggregatorV3Interface_$85", + "typeIdentifier": "t_contract$_AggregatorV3Interface_$660", "typeString": "contract AggregatorV3Interface" } }, - "id": 32, + "id": 145, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberName": "latestRoundData", "nodeType": "MemberAccess", - "referencedDeclaration": 84, - "src": "722:25:1", + "referencedDeclaration": 659, + "src": "848:25:12", "typeDescriptions": { "typeIdentifier": "t_function_external_view$__$returns$_t_uint80_$_t_int256_$_t_uint256_$_t_uint256_$_t_uint80_$", "typeString": "function () view external returns (uint80,int256,uint256,uint256,uint80)" } }, - "id": 33, + "id": 146, "isConstant": false, "isLValue": false, "isPure": false, @@ -423,7 +454,7 @@ "lValueRequested": false, "names": [], "nodeType": "FunctionCall", - "src": "722:27:1", + "src": "848:27:12", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$_t_uint80_$_t_int256_$_t_uint256_$_t_uint256_$_t_uint80_$", @@ -431,37 +462,37 @@ } }, "nodeType": "VariableDeclarationStatement", - "src": "565:184:1" + "src": "691:184:12" }, { "expression": { "argumentTypes": null, - "id": 35, + "id": 148, "name": "price", "nodeType": "Identifier", "overloadedDeclarations": [], - "referencedDeclaration": 24, - "src": "766:5:1", + "referencedDeclaration": 137, + "src": "892:5:12", "typeDescriptions": { "typeIdentifier": "t_int256", "typeString": "int256" } }, - "functionReturnParameters": 20, - "id": 36, + "functionReturnParameters": 133, + "id": 149, "nodeType": "Return", - "src": "759:12:1" + "src": "885:12:12" } ] }, "documentation": { - "id": 16, + "id": 129, "nodeType": "StructuredDocumentation", - "src": "455:43:1", + "src": "581:43:12", "text": " Returns the latest price" }, "functionSelector": "8e15f473", - "id": 38, + "id": 151, "implemented": true, "kind": "function", "modifiers": [], @@ -469,24 +500,24 @@ "nodeType": "FunctionDefinition", "overrides": null, "parameters": { - "id": 17, + "id": 130, "nodeType": "ParameterList", "parameters": [], - "src": "526:2:1" + "src": "652:2:12" }, "returnParameters": { - "id": 20, + "id": 133, "nodeType": "ParameterList", "parameters": [ { "constant": false, - "id": 19, + "id": 132, "mutability": "mutable", "name": "", "nodeType": "VariableDeclaration", "overrides": null, - "scope": 38, - "src": "550:3:1", + "scope": 151, + "src": "676:3:12", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { @@ -494,10 +525,10 @@ "typeString": "int256" }, "typeName": { - "id": 18, + "id": 131, "name": "int", "nodeType": "ElementaryTypeName", - "src": "550:3:1", + "src": "676:3:12", "typeDescriptions": { "typeIdentifier": "t_int256", "typeString": "int256" @@ -507,23 +538,23 @@ "visibility": "internal" } ], - "src": "549:5:1" + "src": "675:5:12" }, - "scope": 39, - "src": "503:275:1", + "scope": 152, + "src": "629:275:12", "stateMutability": "view", "virtual": false, "visibility": "public" } ], - "scope": 40, - "src": "137:643:1" + "scope": 153, + "src": "137:769:12" } ], - "src": "34:746:1" + "src": "34:872:12" }, - "bytecode": "608060405234801561001057600080fd5b50600080546001600160a01b031916739326bfa02add2366b30bacb125260af641031331179055610106806100466000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80638e15f47314602d575b600080fd5b60336045565b60408051918252519081900360200190f35b60008060008060008060008054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015609957600080fd5b505afa15801560ac573d6000803e3d6000fd5b505050506040513d60a081101560c157600080fd5b5060200151955050505050509056fea2646970667358221220c583b49c9d71480c0cc18eae752144f18380cc773a81024c2bd8dd6743a6b01264736f6c634300060c0033", - "bytecodeSha1": "0dfcfca242489dfd236ae92cd20976221bc75e7a", + "bytecode": "608060405234801561001057600080fd5b5060405161016b38038061016b8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055610106806100656000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80638e15f47314602d575b600080fd5b60336045565b60408051918252519081900360200190f35b60008060008060008060008054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015609957600080fd5b505afa15801560ac573d6000803e3d6000fd5b505050506040513d60a081101560c157600080fd5b5060200151955050505050509056fea2646970667358221220bdcd7080aa1805196370133e9ce6d0a57fb342952ab63de0d95648397762e58e64736f6c634300060c0033", + "bytecodeSha1": "097c6bd07620966d0b130e245bf47a3c430dec7a", "compiler": { "evm_version": "istanbul", "optimizer": { @@ -535,15 +566,15 @@ "contractName": "PriceFeed", "coverageMap": { "branches": { - "1": {} + "12": {} }, "statements": { - "1": {} + "12": {} } }, "dependencies": [], - "deployedBytecode": "6080604052348015600f57600080fd5b506004361060285760003560e01c80638e15f47314602d575b600080fd5b60336045565b60408051918252519081900360200190f35b60008060008060008060008054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015609957600080fd5b505afa15801560ac573d6000803e3d6000fd5b505050506040513d60a081101560c157600080fd5b5060200151955050505050509056fea2646970667358221220c583b49c9d71480c0cc18eae752144f18380cc773a81024c2bd8dd6743a6b01264736f6c634300060c0033", - "deployedSourceMap": "137:643:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;503:275;;;:::i;:::-;;;;;;;;;;;;;;;;;550:3;579:14;608:9;631:14;659;687:22;722:9;;;;;;;;-1:-1:-1;;;;;722:9:1;-1:-1:-1;;;;;722:25:1;;:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;722:27:1;;;;-1:-1:-1;;;;;;503:275:1;:::o", + "deployedBytecode": "6080604052348015600f57600080fd5b506004361060285760003560e01c80638e15f47314602d575b600080fd5b60336045565b60408051918252519081900360200190f35b60008060008060008060008054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015609957600080fd5b505afa15801560ac573d6000803e3d6000fd5b505050506040513d60a081101560c157600080fd5b5060200151955050505050509056fea2646970667358221220bdcd7080aa1805196370133e9ce6d0a57fb342952ab63de0d95648397762e58e64736f6c634300060c0033", + "deployedSourceMap": "137:769:12:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;629:275;;;:::i;:::-;;;;;;;;;;;;;;;;;676:3;705:14;734:9;757:14;785;813:22;848:9;;;;;;;;-1:-1:-1;;;;;848:9:12;-1:-1:-1;;;;;848:25:12;;:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;848:27:12;;;;-1:-1:-1;;;;;;629:275:12;:::o", "language": "Solidity", "natspec": { "kind": "dev", @@ -556,625 +587,625 @@ }, "offset": [ 137, - 780 + 906 ], - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x28 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x8E15F473 EQ PUSH1 0x2D JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x33 PUSH1 0x45 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB AND PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB AND PUSH4 0xFEAF968C PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0xA0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH1 0x99 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH1 0xAC JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0xA0 DUP2 LT ISZERO PUSH1 0xC1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 ADD MLOAD SWAP6 POP POP POP POP POP POP SWAP1 JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xC5 DUP4 0xB4 SWAP13 SWAP14 PUSH18 0x480C0CC18EAE752144F18380CC773A81024C 0x2B 0xD8 0xDD PUSH8 0x43A6B01264736F6C PUSH4 0x4300060C STOP CALLER ", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x28 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x8E15F473 EQ PUSH1 0x2D JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x33 PUSH1 0x45 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 SLOAD SWAP1 PUSH2 0x100 EXP SWAP1 DIV PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB AND PUSH1 0x1 PUSH1 0x1 PUSH1 0xA0 SHL SUB AND PUSH4 0xFEAF968C PUSH1 0x40 MLOAD DUP2 PUSH4 0xFFFFFFFF AND PUSH1 0xE0 SHL DUP2 MSTORE PUSH1 0x4 ADD PUSH1 0xA0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP7 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH1 0x99 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS STATICCALL ISZERO DUP1 ISZERO PUSH1 0xAC JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0xA0 DUP2 LT ISZERO PUSH1 0xC1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 ADD MLOAD SWAP6 POP POP POP POP POP POP SWAP1 JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xBD 0xCD PUSH17 0x80AA1805196370133E9CE6D0A57FB34295 0x2A 0xB6 RETURNDATASIZE 0xE0 0xD9 JUMP 0x48 CODECOPY PUSH24 0x62E58E64736F6C634300060C003300000000000000000000 ", "pcMap": { "0": { "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x80" }, "2": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x40" }, "4": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "MSTORE", - "path": "1" + "path": "12" }, "5": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "CALLVALUE", - "path": "1" + "path": "12" }, "6": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "DUP1", - "path": "1" + "path": "12" }, "7": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "ISZERO", - "path": "1" + "path": "12" }, "8": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xF" }, "10": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "JUMPI", - "path": "1" + "path": "12" }, "11": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "13": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "DUP1", - "path": "1" + "path": "12" }, "14": { "dev": "Cannot send ether to nonpayable function", "fn": null, "offset": [ 137, - 780 + 906 ], "op": "REVERT", - "path": "1" + "path": "12" }, "15": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "16": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "POP", - "path": "1" + "path": "12" }, "17": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x4" }, "19": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "CALLDATASIZE", - "path": "1" + "path": "12" }, "20": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "LT", - "path": "1" + "path": "12" }, "21": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x28" }, "23": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "JUMPI", - "path": "1" + "path": "12" }, "24": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "26": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "CALLDATALOAD", - "path": "1" + "path": "12" }, "27": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xE0" }, "29": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "SHR", - "path": "1" + "path": "12" }, "30": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "DUP1", - "path": "1" + "path": "12" }, "31": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH4", - "path": "1", + "path": "12", "value": "0x8E15F473" }, "36": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "EQ", - "path": "1" + "path": "12" }, "37": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x2D" }, "39": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "JUMPI", - "path": "1" + "path": "12" }, "40": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "41": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "43": { "fn": null, "offset": [ 137, - 780 + 906 ], "op": "DUP1", - "path": "1" + "path": "12" }, "44": { "first_revert": true, "fn": null, "offset": [ 137, - 780 + 906 ], "op": "REVERT", - "path": "1" + "path": "12" }, "45": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "46": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x33" }, "48": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x45" }, "50": { "fn": "PriceFeed.getLatestPrice", "jump": "i", "offset": [ - 503, - 778 + 629, + 904 ], "op": "JUMP", - "path": "1" + "path": "12" }, "51": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "52": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x40" }, "54": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "DUP1", - "path": "1" + "path": "12" }, "55": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "MLOAD", - "path": "1" + "path": "12" }, "56": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "SWAP2", - "path": "1" + "path": "12" }, "57": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "DUP3", - "path": "1" + "path": "12" }, "58": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "MSTORE", - "path": "1" + "path": "12" }, "59": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "MLOAD", - "path": "1" + "path": "12" }, "60": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "SWAP1", - "path": "1" + "path": "12" }, "61": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "DUP2", - "path": "1" + "path": "12" }, "62": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "SWAP1", - "path": "1" + "path": "12" }, "63": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "SUB", - "path": "1" + "path": "12" }, "64": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x20" }, "66": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "ADD", - "path": "1" + "path": "12" }, "67": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "SWAP1", - "path": "1" + "path": "12" }, "68": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "RETURN", - "path": "1" + "path": "12" }, "69": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "70": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 550, - 553 + 676, + 679 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "72": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 579, - 593 + 705, + 719 ], "op": "DUP1", - "path": "1" + "path": "12" }, "73": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 608, - 617 + 734, + 743 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "75": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 631, - 645 + 757, + 771 ], "op": "DUP1", - "path": "1" + "path": "12" }, "76": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 659, - 673 + 785, + 799 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "78": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 687, - 709 + 813, + 835 ], "op": "DUP1", - "path": "1" + "path": "12" }, "79": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "81": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "DUP1", - "path": "1" + "path": "12" }, "82": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "SLOAD", - "path": "1" + "path": "12" }, "83": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "SWAP1", - "path": "1" + "path": "12" }, "84": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "PUSH2", - "path": "1", + "path": "12", "value": "0x100" }, "87": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "EXP", - "path": "1" + "path": "12" }, "88": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "SWAP1", - "path": "1" + "path": "12" }, "89": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "DIV", - "path": "1" + "path": "12" }, "90": { "op": "PUSH1", @@ -1197,11 +1228,11 @@ "98": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 731 + 848, + 857 ], "op": "AND", - "path": "1" + "path": "12" }, "99": { "op": "PUSH1", @@ -1224,603 +1255,603 @@ "107": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 747 + 848, + 873 ], "op": "AND", - "path": "1" + "path": "12" }, "108": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 747 + 848, + 873 ], "op": "PUSH4", - "path": "1", + "path": "12", "value": "0xFEAF968C" }, "113": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x40" }, "115": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "MLOAD", - "path": "1" + "path": "12" }, "116": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP2", - "path": "1" + "path": "12" }, "117": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH4", - "path": "1", + "path": "12", "value": "0xFFFFFFFF" }, "122": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "AND", - "path": "1" + "path": "12" }, "123": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xE0" }, "125": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "SHL", - "path": "1" + "path": "12" }, "126": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP2", - "path": "1" + "path": "12" }, "127": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "MSTORE", - "path": "1" + "path": "12" }, "128": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x4" }, "130": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ADD", - "path": "1" + "path": "12" }, "131": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xA0" }, "133": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x40" }, "135": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "MLOAD", - "path": "1" + "path": "12" }, "136": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "137": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP4", - "path": "1" + "path": "12" }, "138": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "SUB", - "path": "1" + "path": "12" }, "139": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP2", - "path": "1" + "path": "12" }, "140": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP7", - "path": "1" + "path": "12" }, "141": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "142": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "EXTCODESIZE", - "path": "1" + "path": "12" }, "143": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ISZERO", - "path": "1" + "path": "12" }, "144": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "145": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ISZERO", - "path": "1" + "path": "12" }, "146": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x99" }, "148": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "JUMPI", - "path": "1" + "path": "12" }, "149": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "151": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "152": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "REVERT", - "path": "1" + "path": "12" }, "153": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "154": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "POP", - "path": "1" + "path": "12" }, "155": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "GAS", - "path": "1" + "path": "12" }, "156": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "STATICCALL", - "path": "1" + "path": "12" }, "157": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ISZERO", - "path": "1" + "path": "12" }, "158": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "159": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ISZERO", - "path": "1" + "path": "12" }, "160": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xAC" }, "162": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "JUMPI", - "path": "1" + "path": "12" }, "163": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "RETURNDATASIZE", - "path": "1" + "path": "12" }, "164": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "166": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "167": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "RETURNDATACOPY", - "path": "1" + "path": "12" }, "168": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "RETURNDATASIZE", - "path": "1" + "path": "12" }, "169": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "171": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "REVERT", - "path": "1" + "path": "12" }, "172": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "173": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "POP", - "path": "1" + "path": "12" }, "174": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "POP", - "path": "1" + "path": "12" }, "175": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "POP", - "path": "1" + "path": "12" }, "176": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "POP", - "path": "1" + "path": "12" }, "177": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x40" }, "179": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "MLOAD", - "path": "1" + "path": "12" }, "180": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "RETURNDATASIZE", - "path": "1" + "path": "12" }, "181": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xA0" }, "183": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP2", - "path": "1" + "path": "12" }, "184": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "LT", - "path": "1" + "path": "12" }, "185": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ISZERO", - "path": "1" + "path": "12" }, "186": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0xC1" }, "188": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "JUMPI", - "path": "1" + "path": "12" }, "189": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x0" }, "191": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "DUP1", - "path": "1" + "path": "12" }, "192": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "REVERT", - "path": "1" + "path": "12" }, "193": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "JUMPDEST", - "path": "1" + "path": "12" }, "194": { "op": "POP" @@ -1828,39 +1859,39 @@ "195": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "PUSH1", - "path": "1", + "path": "12", "value": "0x20" }, "197": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "ADD", - "path": "1" + "path": "12" }, "198": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "MLOAD", - "path": "1" + "path": "12" }, "199": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 722, - 749 + 848, + 875 ], "op": "SWAP6", - "path": "1" + "path": "12" }, "200": { "op": "POP" @@ -1883,26 +1914,26 @@ "206": { "fn": "PriceFeed.getLatestPrice", "offset": [ - 503, - 778 + 629, + 904 ], "op": "SWAP1", - "path": "1" + "path": "12" }, "207": { "fn": "PriceFeed.getLatestPrice", "jump": "o", "offset": [ - 503, - 778 + 629, + 904 ], "op": "JUMP", - "path": "1" + "path": "12" } }, - "sha1": "92f7a2ca225b43133050f15f2febe4ed55b2201f", - "source": "\n// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.7;\n\nimport \"@chainlink-contracts/src/v0.6/interfaces/AggregatorV3Interface.sol\";\n\ncontract PriceFeed {\n\n AggregatorV3Interface internal priceFeed;\n\n /**\n * Network: Kovan\n * Aggregator: ETH/USD\n * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331\n */\n constructor() public {\n priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);\n }\n\n /**\n * Returns the latest price\n */\n function getLatestPrice() public view returns (int) {\n (\n uint80 roundID, \n int price,\n uint startedAt,\n uint timeStamp,\n uint80 answeredInRound\n ) = priceFeed.latestRoundData();\n return price;\n }\n}", - "sourceMap": "137:643:1:-:0;;;334:115;;;;;;;;;-1:-1:-1;365:9:1;:77;;-1:-1:-1;;;;;;365:77:1;399:42;365:77;;;137:643;;;;;;", + "sha1": "c32188124f5ad5c69439e17c92b3610a3b67cc1d", + "source": "\n// SPDX-License-Identifier: MIT\n\npragma solidity ^0.6.6;\n\nimport \"@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol\";\n\ncontract PriceFeed {\n\n AggregatorV3Interface internal priceFeed;\n\n /**\n * Network: Kovan\n * Aggregator: ETH/USD\n * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331\n */\n /**\n * Network: Mainnet\n * Aggregator: ETH/USD\n * Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419\n */\n constructor(address AggregatorAddress) public {\n priceFeed = AggregatorV3Interface(AggregatorAddress);\n }\n\n /**\n * Returns the latest price\n */\n function getLatestPrice() public view returns (int) {\n (\n uint80 roundID, \n int price,\n uint startedAt,\n uint timeStamp,\n uint80 answeredInRound\n ) = priceFeed.latestRoundData();\n return price;\n }\n}", + "sourceMap": "137:769:12:-:0;;;460:115;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;460:115:12;516:9;:52;;-1:-1:-1;;;;;516:52:12;;;-1:-1:-1;;;;;;516:52:12;;;;;;;;;137:769;;;;;;", "sourcePath": "contracts/PriceFeed.sol", "type": "contract" } \ No newline at end of file diff --git a/build/deployments/map.json b/build/deployments/map.json index 9e26dfe..16ed8be 100644 --- a/build/deployments/map.json +++ b/build/deployments/map.json @@ -1 +1,80 @@ -{} \ No newline at end of file +{ + "42": { + "APIConsumer": [ + "0x798158B1bb7E01ac613898111a115A629A33Bd9D", + "0x377b0995dBD8ace966109D131827029aa50Db814", + "0xDaD05755DB9D102e611c515eF19b75dF25f890B9", + "0xa03A4Bc9a1ef6676257759D4F8d4518082D995C9", + "0xfC1B75f8b62b517081f27F716C03c50eB64fF0Cf", + "0x9b1F72cAe44c4DE65a43AF383Dfab88aA7e37b6A", + "0x5574AeC48305fABFd5fc57C3Ad6e23FBBcc468Ee", + "0x89F6Ed7563F91AC390aa5Cb3c6DE84085b86E5f7", + "0x694cB5C8585572E60FfabE1223793Fc5A5F59bA6", + "0x0E87157D7bB50073b0544B79D447999087F3Af90", + "0x0631c78BbeeC2bC077c6D993CD0c48AF57cDc95C", + "0x61f31404097bFa51AfEaFBF8Ff61B5624936Db68", + "0x2F9665885636fbaa69df2DD0E6F3BBA9099194C6", + "0x0B10aC59B5CC30dDD050Dfe0793680248589B3A1", + "0x2C5c72842a8CC72Ab50A2D189e342e4280618B61", + "0x97c3F348bc285635312fF4072e210B32A5cdA58E", + "0x04A77B299Bf482C627e0EdF8ca014C8CB5c06C8a", + "0x0B82583213906CF767DBf6046a77187370631fAE", + "0x4fB82F36c46dC58A42D2361AC3326D7a706F30A5", + "0x6145388f112e4054B553C89f2747f765d6545832", + "0xb4c5E0c078953Bd1Be1f80dcbB4A10F9f180bBc8" + ], + "KovanPriceFeed": [ + "0x6075fB820845bB3B0DC11aE314ec9DC90C23F259" + ], + "PriceFeed": [ + "0x70B28b0e828B2b1f2668609A624093E6fB081cd9", + "0xEdA8f247d0dC35b8F39f13596BB9bE03A9737e0B", + "0xa99c55aaB529cD49a7f3b322c844bab30C4a4071", + "0xD245e47fec12846219a1B792971476E2A0caa46c", + "0xb03B88b50602b15D0Aa26071758E38348eFf04C7", + "0x6a25aA8c78760d008867ce9047D9B6052548E75E", + "0x6b56393ADdf547Ef294994D06639cb81df250748", + "0xAd362C913b63F8e6979cB4439d30B2939b2AAbe6" + ], + "VRFConsumer": [ + "0x4AFcAEBf47D9C5f8B4FC352C2b4824133C053BaF", + "0xf4FDaA32738330383e06D39D8D1291aa6d3A1084", + "0x9286a57908Bf116633a25A791123797a352baD6a", + "0x151ca42f1EB4889a0Ff6B939d12e3f2D4f726C85", + "0x9871Fd9424e2E968e82683Cdc80eE8bBb707739f", + "0x29ef037721AdCbA8a9105696C2E4d4A165757125", + "0xf6dE833c682FC283DCDaade20059394f9FD041C9", + "0x1F3840AB82504CE438746F14d567eAC4B0168908", + "0xBC227229e90f43019a614Be471b84B46deF61774", + "0x01383a8D406E0d1bCbbA2E1265bB3F7Bc20B0b15", + "0x8923fcFb9e637140aC2e89bDAd859b091aBA14d0", + "0x9668Fde59Bac7A470ea1f13a4Aaf57C95545dd2C", + "0xd9a854ed462a62A51230904098Ec00037e2d4D6F", + "0x0D7927B6b08ecfb1adBf982859e9C00a9fdFCCA2", + "0x497Ba62FdAF65E16A86119BC80A95985ad6D0efF", + "0x3B4Cf06A51608b20524152b43463a7EB39D10041", + "0x1B9ef7f72B5E9A755F24AC6Dab94Cfd6e2eeDf74", + "0x81b9B93201eED933D213A3cCe07741564Dd4057F", + "0x0108c1AcdE28bF3E08F0AC882eDa74bb518DF186", + "0xf35e8b7DBF59E352e16F66Bb874A83F89676528C", + "0x93919B6b70086dc5EA2FB5A9979bff03b87Ae366", + "0xFb2B6e46e10Fb3E983e166c14eB8a95c735CcAa6", + "0x2c25BF804A2EA7fd1ccEec82667c380F196E934A", + "0x220305557B4d1A4D708585379eB511D47D9Add2F", + "0x592b2030Cf071Cd0Cac916C7Ef6b515F591746eF", + "0x611c1bb52aC6839D0830CBFb2a6Dba7e906E674b", + "0x74745c87A975f7C72b000BF150d708903457747a", + "0x9444927314b9280d32DC14c71f5B7A08857cc56E", + "0x368da205DFA38CF1b75261Bd06449b1BaffCC1d8", + "0xF4fADf92556082a0C5D49348Eb0828bd2259e604", + "0xD716EC66a5c494AAf28f66FCEe21e1941d2D7CAd", + "0x6b05c626772c1e4607D8a992D5DE8D531d43DDd0", + "0x6C2997649A4293b5C822dDB5544d39457089e9A1", + "0x5Ee86a0B022b929D705A3e502F2e3aF55F5bb25E", + "0xB302e73f4b2C4e6dD8Bd21C99e676a6301D5F0A0", + "0x9c5CF715b6C0D2aC3DAE88A44119575e139Bb8b9", + "0xC0a987fbe51847Ed02D2ca3B421F73Ea81098194", + "0x6361A70daD57465C1Db4A5179fD569EcF14E0eaA" + ] + } +} \ No newline at end of file diff --git a/build/tests.json b/build/tests.json index 16c50c9..252eea6 100644 --- a/build/tests.json +++ b/build/tests.json @@ -1,13 +1,35 @@ { "contracts": { - "PriceFeed": "0dfcfca242489dfd236ae92cd20976221bc75e7a" + "APIConsumer": "e0d10735505a008e83a98587044e521efb4c3aef", + "PriceFeed": "097c6bd07620966d0b130e245bf47a3c430dec7a", + "VRFConsumer": "381da532cf629be9dd09a19881491f8ddf045863", + "alphachainio/chainlink-contracts@1.1.0/BufferChainlink": "3b4158955f564edb18bd7a8ffdb49859c904f0b7", + "alphachainio/chainlink-contracts@1.1.0/CBORChainlink": "3b4158955f564edb18bd7a8ffdb49859c904f0b7", + "alphachainio/chainlink-contracts@1.1.0/Chainlink": "3b4158955f564edb18bd7a8ffdb49859c904f0b7", + "alphachainio/chainlink-contracts@1.1.0/ChainlinkClient": "750a107dfd09b0702b5ae03a4b6ea3c8b73190e1", + "alphachainio/chainlink-contracts@1.1.0/SafeMath": "3b4158955f564edb18bd7a8ffdb49859c904f0b7", + "alphachainio/chainlink-contracts@1.1.0/VRFRequestIDBase": "73df0a6f5d0123eb2b2db0faf62b602aa77c123a" }, "tests": { - "tests/test_price_feed.py": { + "tests/mainnet-fork/test_mainnet_fork_price_feed.py": { + "coverage": false, + "isolated": false, + "results": "ss", + "sha1": "cbd8253c71bb070e447f9b141ccfa364e280099e", + "txhash": [] + }, + "tests/staging/test_kovan_api.py": { "coverage": false, "isolated": false, "results": "F", - "sha1": "d44dbdd689a7ca401eef62381ca36405ab74ac27", + "sha1": "7a8c750de2735a5bce259d1a82758d1a0d85a38e", + "txhash": [] + }, + "tests/staging/test_kovan_vrf.py": { + "coverage": false, + "isolated": false, + "results": ".", + "sha1": "c5106529961c168f0890512592e5ad02d5161709", "txhash": [] } }, diff --git a/contracts/APIConsumer.sol b/contracts/APIConsumer.sol new file mode 100644 index 0000000..2b0f0c0 --- /dev/null +++ b/contracts/APIConsumer.sol @@ -0,0 +1,78 @@ +pragma solidity ^0.6.6; + +import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol"; + +contract APIConsumer is ChainlinkClient { + + uint256 public volume; + + address private oracle; + bytes32 private jobId; + uint256 private fee; + + /** + * Network: Kovan + * Oracle: 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e + * Job ID: 29fa9aa13bf1468788b7cc4a500a45b8 + * Fee: 0.1 LINK + */ + constructor(address _oracle, string memory _jobId, uint256 _fee) public { + setPublicChainlinkToken(); + // oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e; + // jobId = "29fa9aa13bf1468788b7cc4a500a45b8"; + // fee = 0.1 * 10 ** 18; // 0.1 LINK + oracle = _oracle; + jobId = stringToBytes32(_jobId); + fee = _fee; + } + + /** + * Create a Chainlink request to retrieve API response, find the target + * data, then multiply by 1000000000000000000 (to remove decimal places from data). + */ + function requestVolumeData() public returns (bytes32 requestId) + { + Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector); + + // Set the URL to perform the GET request on + request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD"); + + // Set the path to find the desired data in the API response, where the response format is: + // {"RAW": + // {"ETH": + // {"USD": + // { + // "VOLUME24HOUR": xxx.xxx, + // } + // } + // } + // } + request.add("path", "RAW.ETH.USD.VOLUME24HOUR"); + + // Multiply the result by 1000000000000000000 to remove decimals + int timesAmount = 10**18; + request.addInt("times", timesAmount); + + // Sends the request + return sendChainlinkRequestTo(oracle, request, fee); + } + + /** + * Receive the response in the form of uint256 + */ + function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId) + { + volume = _volume; + } + + function stringToBytes32(string memory source) public pure returns (bytes32 result) { + bytes memory tempEmptyStringTest = bytes(source); + if (tempEmptyStringTest.length == 0) { + return 0x0; + } + + assembly { + result := mload(add(source, 32)) + } + } +} \ No newline at end of file diff --git a/contracts/PriceFeed.sol b/contracts/PriceFeed.sol index 1dee4f5..fe28085 100644 --- a/contracts/PriceFeed.sol +++ b/contracts/PriceFeed.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.6.7; +pragma solidity ^0.6.6; -import "@chainlink-contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; +import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; contract PriceFeed { @@ -14,8 +14,13 @@ contract PriceFeed { * Aggregator: ETH/USD * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331 */ - constructor() public { - priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331); + /** + * Network: Mainnet + * Aggregator: ETH/USD + * Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 + */ + constructor(address AggregatorAddress) public { + priceFeed = AggregatorV3Interface(AggregatorAddress); } /** diff --git a/contracts/VRFConsumer.sol b/contracts/VRFConsumer.sol new file mode 100644 index 0000000..08334c6 --- /dev/null +++ b/contracts/VRFConsumer.sol @@ -0,0 +1,45 @@ + +pragma solidity 0.6.6; + +import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + +contract VRFConsumer is VRFConsumerBase { + + bytes32 internal keyHash; + uint256 internal fee; + + uint256 public randomResult; + + /** + * Constructor inherits VRFConsumerBase + * + * Network: Kovan + * Chainlink VRF Coordinator address: 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9 + * LINK token address: 0xa36085F69e2889c224210F603D836748e7dC0088 + * Key Hash: 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4 + */ + constructor(bytes32 _keyhash, address _vrfCoordinator, address _linkToken) + VRFConsumerBase( + _vrfCoordinator, // VRF Coordinator + _linkToken // LINK Token + ) public + { + keyHash = _keyhash; + fee = 0.1 * 10 ** 18; // 0.1 LINK + } + + /** + * Requests randomness from a user-provided seed + */ + function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) { + require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet"); + return requestRandomness(keyHash, fee, userProvidedSeed); + } + + /** + * Callback function used by VRF Coordinator + */ + function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { + randomResult = randomness; + } +} \ No newline at end of file diff --git a/interfaces/LinkTokenInterface.sol b/interfaces/LinkTokenInterface.sol new file mode 100644 index 0000000..576e0b6 --- /dev/null +++ b/interfaces/LinkTokenInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.6.6; + +interface LinkTokenInterface { + function allowance(address owner, address spender) external view returns (uint256 remaining); + function approve(address spender, uint256 value) external returns (bool success); + function balanceOf(address owner) external view returns (uint256 balance); + function decimals() external view returns (uint8 decimalPlaces); + function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); + function increaseApproval(address spender, uint256 subtractedValue) external; + function name() external view returns (string memory tokenName); + function symbol() external view returns (string memory tokenSymbol); + function totalSupply() external view returns (uint256 totalTokensIssued); + function transfer(address to, uint256 value) external returns (bool success); + function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); + function transferFrom(address from, address to, uint256 value) external returns (bool success); +} \ No newline at end of file diff --git a/scripts/chainlink_api_scripts/deploy_api_consumer.py b/scripts/chainlink_api_scripts/deploy_api_consumer.py new file mode 100644 index 0000000..e8d40a8 --- /dev/null +++ b/scripts/chainlink_api_scripts/deploy_api_consumer.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +import os +from brownie import APIConsumer, accounts, network, config + + +def main(): + dev = accounts.add(os.getenv(config['wallets']['from_key'])) + return APIConsumer.deploy( + config['networks'][network.show_active()]['oracle'], + config['networks'][network.show_active()]['jobId'], + config['networks'][network.show_active()]['fee'], + {'from': dev}) diff --git a/scripts/chainlink_api_scripts/fund_chainlink_api.py b/scripts/chainlink_api_scripts/fund_chainlink_api.py new file mode 100644 index 0000000..f4abad2 --- /dev/null +++ b/scripts/chainlink_api_scripts/fund_chainlink_api.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +import os +from brownie import APIConsumer, accounts, network, interface, config + + +def main(): + dev = accounts.add(os.getenv('PRIVATE_KEY')) + # Get the most recent PriceFeed Object + api_contract = APIConsumer[len(APIConsumer) - 1] + interface.LinkTokenInterface(config['networks'][network.show_active()]['link_token']).transfer( + api_contract, 1000000000000000000, {'from': dev}) + print("Funded {}".format(api_contract.address)) diff --git a/scripts/chainlink_api_scripts/read_data.py b/scripts/chainlink_api_scripts/read_data.py new file mode 100644 index 0000000..29264b2 --- /dev/null +++ b/scripts/chainlink_api_scripts/read_data.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +from brownie import APIConsumer + + +def main(): + api_contract = APIConsumer[len(APIConsumer) - 1] + print("Reading data from {}".format(api_contract.address)) + print(api_contract.volume()) diff --git a/scripts/chainlink_api_scripts/request_api.py b/scripts/chainlink_api_scripts/request_api.py new file mode 100644 index 0000000..fb68c32 --- /dev/null +++ b/scripts/chainlink_api_scripts/request_api.py @@ -0,0 +1,10 @@ +#!/usr/bin/python3 +import os +from brownie import APIConsumer, accounts, config + + +def main(): + dev = accounts.add(os.getenv(config['wallets']['from_key'])) + # Get the most recent PriceFeed Object + api_contract = APIConsumer[len(APIConsumer) - 1] + api_contract.requestVolumeData({'from': dev}) diff --git a/scripts/deploy_price_consumer_v3.py b/scripts/deploy_price_consumer_v3.py deleted file mode 100644 index aae04a1..0000000 --- a/scripts/deploy_price_consumer_v3.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/python3 - -from brownie import PriceFeed, accounts - - -def main(): - return PriceFeed.deploy({'from': accounts[0]}) diff --git a/scripts/price_feed_scripts/deploy_price_consumer_v3.py b/scripts/price_feed_scripts/deploy_price_consumer_v3.py new file mode 100644 index 0000000..a899aec --- /dev/null +++ b/scripts/price_feed_scripts/deploy_price_consumer_v3.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +import os +from brownie import PriceFeed, accounts, network + +mainnet_eth_usd_address = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419' +kovan_eth_usd_address = '0x9326BFA02ADD2366b30bacB125260Af641031331' + + +def main(): + if network.show_active() == 'mainnet-fork' or network.show_active() == 'development': + return PriceFeed.deploy(mainnet_eth_usd_address, {'from': accounts[0]}) + elif network.show_active() == 'kovan': + dev = accounts.add(os.getenv('PRIVATE_KEY')) + return PriceFeed.deploy(kovan_eth_usd_address, {'from': dev}) + else: + print('Please pick a supported network, or add the rinkeby config') diff --git a/scripts/price_feed_scripts/read_price_feed.py b/scripts/price_feed_scripts/read_price_feed.py new file mode 100644 index 0000000..751f9c9 --- /dev/null +++ b/scripts/price_feed_scripts/read_price_feed.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +from brownie import PriceFeed + + +def main(): + price_feed_contract = PriceFeed[len(PriceFeed) - 1] + print("Reading data from {}".format(price_feed_contract.address)) + print(price_feed_contract.getLatestPrice()) diff --git a/scripts/vrf_scripts/deploy_vrf.py b/scripts/vrf_scripts/deploy_vrf.py new file mode 100644 index 0000000..236f2c4 --- /dev/null +++ b/scripts/vrf_scripts/deploy_vrf.py @@ -0,0 +1,14 @@ +#!/usr/bin/python3 +import os +from brownie import VRFConsumer, accounts, network, config + + +def main(): + dev = accounts.add(os.getenv(config['wallets']['from_key'])) + print(network.show_active()) + return VRFConsumer.deploy(config['networks'][network.show_active()]['keyhash'], + config['networks'][network.show_active() + ]['vrf_coordinator'], + config['networks'][network.show_active() + ]['link_token'], + {'from': dev}) diff --git a/scripts/vrf_scripts/fund_vrf.py b/scripts/vrf_scripts/fund_vrf.py new file mode 100644 index 0000000..d041828 --- /dev/null +++ b/scripts/vrf_scripts/fund_vrf.py @@ -0,0 +1,11 @@ +#!/usr/bin/python3 +import os +from brownie import VRFConsumer, accounts, network, interface, config + + +def main(): + dev = accounts.add(os.getenv(config['wallets']['from_key'])) + # Get the most recent PriceFeed Object + vrf_contract = VRFConsumer[len(VRFConsumer) - 1] + interface.LinkTokenInterface(config['networks'][network.show_active()]['link_token']).transfer( + vrf_contract, 1000000000000000000, {'from': dev}) diff --git a/scripts/vrf_scripts/read_random_number.py b/scripts/vrf_scripts/read_random_number.py new file mode 100644 index 0000000..2dd617a --- /dev/null +++ b/scripts/vrf_scripts/read_random_number.py @@ -0,0 +1,9 @@ +#!/usr/bin/python3 +from brownie import VRFConsumer + +STATIC_SEED = 123 + + +def main(): + vrf_contract = VRFConsumer[len(VRFConsumer) - 1] + print(vrf_contract.randomResult()) diff --git a/scripts/vrf_scripts/request_randomness.py b/scripts/vrf_scripts/request_randomness.py new file mode 100644 index 0000000..57da8d8 --- /dev/null +++ b/scripts/vrf_scripts/request_randomness.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +import os +from brownie import VRFConsumer, accounts, config + +STATIC_SEED = 123 + + +def main(): + dev = accounts.add(os.getenv(config['wallets']['from_key'])) + # Get the most recent PriceFeed Object + vrf_contract = VRFConsumer[len(VRFConsumer) - 1] + vrf_contract.getRandomNumber(STATIC_SEED, {'from': dev}) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..d1d0f75 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,23 @@ + +import pytest +from brownie import config, network, accounts +import os + + +@pytest.fixture +def kovan_eth_usd_address(): + return '0x9326BFA02ADD2366b30bacB125260Af641031331' + + +@pytest.fixture +def mainnet_eth_usd_address(): + return '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419' + + +@pytest.fixture +def get_testnet_account(): + if network.show_active() != 'kovan' and network.show_active() != 'rinkeby': + pytest.skip('Only works for testnets network') + testnet_account = accounts.add( + os.getenv(config['wallets']['from_key'])) + return testnet_account diff --git a/tests/mainnet-fork/test_mainnet_fork_price_feed.py b/tests/mainnet-fork/test_mainnet_fork_price_feed.py new file mode 100644 index 0000000..b44e43a --- /dev/null +++ b/tests/mainnet-fork/test_mainnet_fork_price_feed.py @@ -0,0 +1,25 @@ +import pytest +from brownie import PriceFeed, accounts, network + + +def test_can_deploy_contract(mainnet_eth_usd_address): + # Arrange + if network.show_active() != 'mainnet-fork': + pytest.skip('Only works for mainnet-fork network') + # Act + price_feed = PriceFeed.deploy( + mainnet_eth_usd_address, {'from': accounts[0]}) + # Assert + assert price_feed is not None + + +def test_can_get_latest_price(mainnet_eth_usd_address): + # Arrange + if network.show_active() != 'mainnet-fork': + pytest.skip('Only works for mainnet-fork network') + # Act + price_feed = PriceFeed.deploy( + mainnet_eth_usd_address, {'from': accounts[0]}) + # Assert + value = price_feed.getLatestPrice({'from': accounts[0]}) + assert isinstance(value, int) diff --git a/tests/staging/test_kovan_api.py b/tests/staging/test_kovan_api.py new file mode 100644 index 0000000..5c3ddad --- /dev/null +++ b/tests/staging/test_kovan_api.py @@ -0,0 +1,39 @@ +import pytest +import time +from brownie import APIConsumer, network, config, interface +from flaky import flaky +from web3 import Web3 + + +@pytest.fixture +def deploy_api_contract(get_testnet_account): + # Arrange + if network.show_active() != 'kovan': + pytest.skip('Only works for kovan network') + # Act + api_consumer = APIConsumer.deploy(config['networks'][network.show_active()]['oracle'], + config['networks'][network.show_active() + ]['jobId'], + config['networks'][network.show_active() + ]['fee'], + {'from': get_testnet_account}) + # Assert + assert api_consumer is not None + return api_consumer + + +@flaky +def test_send_api_request(deploy_api_contract, get_testnet_account): + # Arrange + api_contract = deploy_api_contract + if network.show_active() != 'kovan': + pytest.skip('Only works for kovan network') + interface.LinkTokenInterface(config['networks']['kovan']['link_token']).transfer( + api_contract, 1000000000000000000, {'from': get_testnet_account}) + # Act + requestId = api_contract.requestVolumeData({'from': get_testnet_account}) + # Assert + assert requestId is not None + time.sleep(20) + assert isinstance(api_contract.volume(), int) + assert api_contract.volume() > 0 diff --git a/tests/staging/test_kovan_vrf.py b/tests/staging/test_kovan_vrf.py new file mode 100644 index 0000000..baa358f --- /dev/null +++ b/tests/staging/test_kovan_vrf.py @@ -0,0 +1,38 @@ +import pytest +import time +from brownie import VRFConsumer, network, config, interface +from flaky import flaky + +STATIC_RANDOM_SEED = 123 + + +@pytest.fixture +def deploy_vrf_contract(get_testnet_account): + # Arrange + if network.show_active() != 'kovan': + pytest.skip('Only works for kovan network') + # Act + vrf_consumer = VRFConsumer.deploy(config['networks']['kovan']['keyhash'], + config['networks']['kovan']['vrf_coordinator'], + config['networks']['kovan']['link_token'], + {'from': get_testnet_account}) + # Assert + assert vrf_consumer is not None + return vrf_consumer + + +@flaky +def test_send_random_number_request(deploy_vrf_contract, get_testnet_account): + # Arrange + vrf_contract = deploy_vrf_contract + if network.show_active() != 'kovan': + pytest.skip('Only works for kovan network') + interface.LinkTokenInterface(config['networks']['kovan']['link_token']).transfer( + vrf_contract, 1000000000000000000, {'from': get_testnet_account}) + # Act + requestId = vrf_contract.getRandomNumber( + STATIC_RANDOM_SEED, {'from': get_testnet_account}) + # Assert + assert requestId is not None + time.sleep(20) + assert isinstance(vrf_contract.randomResult(), int) diff --git a/tests/test_price_feed.py b/tests/test_price_feed.py deleted file mode 100644 index 1f394bb..0000000 --- a/tests/test_price_feed.py +++ /dev/null @@ -1,8 +0,0 @@ -import pytest -from brownie import PriceFeed - - -def test_transfer(accounts): - price_feed = PriceFeed.deploy({'from': accounts[0]}) - value = price_feed.getLatestPrice({'from': accounts[0]}) - assert isinstance(value, int)