From a6d52b191c95ae64c78c91972d5c6ad086d77387 Mon Sep 17 00:00:00 2001 From: Tovieye Ozi Date: Tue, 4 May 2021 17:29:19 +0100 Subject: [PATCH] sucessful compile --- .env | 3 + .env.example | 3 + .eslintrc.js | 10 + .gitattributes | 2 + .gitignore | 6 + README.md | 9 +- .../CoinPriceOracle.sol | 25 +- contracts/Greeter.sol | 23 + .../MonetaryPolicy.sol | 16 +- .../Orchestrator.sol | 25 +- RebaseToken.sol => contracts/RebaseToken.sol | 6 +- SafeMath.sol => contracts/SafeMath.sol | 3 +- SafeMathInt.sol => contracts/SafeMathInt.sol | 3 +- UInt256Lib.sol => contracts/UInt256Lib.sol | 3 +- contracts/test/LinkToken.sol | 4 + contracts/test/MockOracle.sol | 194 + contracts/test/MockV3Aggregator.sol | 4 + contracts/test/VRFCoordinatorMock.sol | 4 + deploy/00_Deploy_Mocks.js | 38 + deploy/01_Deploy_PriceConsumerV3.js | 30 + deploy/02_Deploy_APIConsumer.js | 40 + deploy/03_Deploy_RandomNumberConsumer.js | 42 + hardhat.config.js | 96 + helper-hardhat-config.js | 57 + logo.png | Bin 0 -> 2884 bytes package.json | 37 + scripts/sample-script.js | 32 + tasks/accounts.js | 9 + tasks/api-consumer/index.js | 2 + tasks/api-consumer/read-data.js | 29 + tasks/api-consumer/request-data.js | 25 + tasks/balance.js | 12 + tasks/block-number.js | 9 + tasks/fund-link.js | 29 + tasks/price-consumer/index.js | 1 + tasks/price-consumer/read-price-feed.js | 22 + tasks/random-number-consumer/index.js | 2 + .../read-random-number.js | 29 + .../request-random-number.js | 28 + test/sample-test.js | 14 + yarn.lock | 10469 ++++++++++++++++ 41 files changed, 11354 insertions(+), 41 deletions(-) create mode 100644 .env create mode 100644 .env.example create mode 100644 .eslintrc.js create mode 100644 .gitattributes create mode 100644 .gitignore rename CoinPriceOracle.sol => contracts/CoinPriceOracle.sol (75%) create mode 100644 contracts/Greeter.sol rename MonetaryPolicy.sol => contracts/MonetaryPolicy.sol (96%) rename Orchestrator.sol => contracts/Orchestrator.sol (85%) rename RebaseToken.sol => contracts/RebaseToken.sol (98%) rename SafeMath.sol => contracts/SafeMath.sol (96%) rename SafeMathInt.sol => contracts/SafeMathInt.sol (96%) rename UInt256Lib.sol => contracts/UInt256Lib.sol (86%) create mode 100644 contracts/test/LinkToken.sol create mode 100644 contracts/test/MockOracle.sol create mode 100644 contracts/test/MockV3Aggregator.sol create mode 100644 contracts/test/VRFCoordinatorMock.sol create mode 100644 deploy/00_Deploy_Mocks.js create mode 100644 deploy/01_Deploy_PriceConsumerV3.js create mode 100644 deploy/02_Deploy_APIConsumer.js create mode 100644 deploy/03_Deploy_RandomNumberConsumer.js create mode 100644 hardhat.config.js create mode 100644 helper-hardhat-config.js create mode 100644 logo.png create mode 100644 package.json create mode 100644 scripts/sample-script.js create mode 100644 tasks/accounts.js create mode 100644 tasks/api-consumer/index.js create mode 100644 tasks/api-consumer/read-data.js create mode 100644 tasks/api-consumer/request-data.js create mode 100644 tasks/balance.js create mode 100644 tasks/block-number.js create mode 100644 tasks/fund-link.js create mode 100644 tasks/price-consumer/index.js create mode 100644 tasks/price-consumer/read-price-feed.js create mode 100644 tasks/random-number-consumer/index.js create mode 100644 tasks/random-number-consumer/read-random-number.js create mode 100644 tasks/random-number-consumer/request-random-number.js create mode 100644 test/sample-test.js create mode 100644 yarn.lock diff --git a/.env b/.env new file mode 100644 index 0000000..7904ad1 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +export KOVAN_RPC_URL='www.infura.io/asdfadsfafdadf' +export PRIVATE_KEY='abcdef' +export ALCHEMY_MAINNET_RPC_URL="https://eth-mainnet.alchemyapi.io/v2/your-api-key" diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..03ce482 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +KOVAN_RPC_URL='https://kovan.infura.io/v3/1234567890' +PRIVATE_KEY='abcdefg' +ALCHEMY_MAINNET_RPC_URL="https://eth-mainnet.alchemyapi.io/v2/your-api-key" diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..e1b95d4 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + "parserOptions": { + "ecmaVersion": 6, + "ecmaFeatures": { + "experimentalObjectRestSpread": true + } + }, + "parser": "babel-eslint" +} + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30e5d04 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode +node_modules + +#Hardhat files +cache +artifacts diff --git a/README.md b/README.md index 2f81fec..6bcf844 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,18 @@ To get a local copy up and running follow these simple example steps. - Fork the repository -- Git clone https://github.com/your-username/bnbCodedClass +- Git clone https://github.com/your-username/MC16Coin - git checkout -b branch name -- git remote add upstream https://github.com/cloud9n/bnbCodedClass +- yarn install +- git remote add upstream https://github.com/MasterClass16/MC16Coin - git pull upstream develop - git commit -m "commit message" - git push -u origin HEAD +## Compiling + + $ npx hardhat compile + ## Author(s) ## 🤝 Contributing diff --git a/CoinPriceOracle.sol b/contracts/CoinPriceOracle.sol similarity index 75% rename from CoinPriceOracle.sol rename to contracts/CoinPriceOracle.sol index 9b1b237..a229086 100644 --- a/CoinPriceOracle.sol +++ b/contracts/CoinPriceOracle.sol @@ -1,6 +1,7 @@ -pragma solidity ^0.7.6; +pragma solidity ^0.6.8; +// SPDX-License-Identifier: MIT -import "https://raw.githubusercontent.com/smartcontractkit/chainlink/master/evm-contracts/src/v0.6/ChainlinkClient.sol"; +import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol"; import "./RebaseToken.sol"; @@ -8,6 +9,7 @@ contract CoinPriceOracle is ChainlinkClient { uint256 public price; + address owner_; address private oracle; bytes32 private jobId; uint256 private fee; @@ -15,15 +17,16 @@ contract CoinPriceOracle is ChainlinkClient { /** - * Network: Kovan - * Chainlink - 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e - * Chainlink - 29fa9aa13bf1468788b7cc4a500a45b8 + * Network: Binance Smart Chain Testnet + * Oracle: Chainlink - 0x3b3D60B4a33B8B8c7798F7B3E8964b76FBE1E176 + * Job ID: Chainlink - 76bea30a605846cea6af93dbae70ed39 * Fee: 0.1 LINK */ constructor() public { + owner_ = msg.sender; setPublicChainlinkToken(); - oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e; - jobId = "29fa9aa13bf1468788b7cc4a500a45b8"; + oracle = 0x3b3D60B4a33B8B8c7798F7B3E8964b76FBE1E176; + jobId = "76bea30a605846cea6af93dbae70ed39"; fee = 0.1 * 10 ** 18; // 0.1 LINK defaultDuration = 60 * 60 * 24; } @@ -33,14 +36,6 @@ contract CoinPriceOracle is ChainlinkClient { require(msg.sender == owner_,"Only the owner of the contract can use"); _; } - - function triggerRebase() external { - rebaseC.rebase(block.timestamp, supplyDelta); - } - - function setRebaseC(RebaseToken addr) external onlyOwner { - rebaseC = addr; - } function requestPriceData() external returns (bytes32 requestId) { diff --git a/contracts/Greeter.sol b/contracts/Greeter.sol new file mode 100644 index 0000000..b758b59 --- /dev/null +++ b/contracts/Greeter.sol @@ -0,0 +1,23 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.6.8; + +import "hardhat/console.sol"; + + +contract Greeter { + string greeting; + + constructor(string memory _greeting) public { + console.log("Deploying a Greeter with greeting:", _greeting); + greeting = _greeting; + } + + function greet() public view returns (string memory) { + return greeting; + } + + function setGreeting(string memory _greeting) public { + console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); + greeting = _greeting; + } +} diff --git a/MonetaryPolicy.sol b/contracts/MonetaryPolicy.sol similarity index 96% rename from MonetaryPolicy.sol rename to contracts/MonetaryPolicy.sol index fa951a9..b316fb8 100644 --- a/MonetaryPolicy.sol +++ b/contracts/MonetaryPolicy.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.7.6; +pragma solidity ^0.6.8; +// SPDX-License-Identifier: MIT import "./SafeMath.sol"; import "./SafeMathInt.sol"; @@ -6,7 +7,7 @@ import "./UInt256Lib.sol"; interface ICoinPriceOracle { - function price() external returns (uint256 price) + function price() external returns (uint256 _price); } interface IRebaseToken { @@ -29,6 +30,7 @@ contract MonetaryPolicy { ); ICoinPriceOracle public coinPrice; + address owner_; IRebaseToken public rebaseToken; // If the current exchange rate is within this fractional distance from the target, no supply @@ -95,13 +97,13 @@ contract MonetaryPolicy { epoch = epoch.add(1); - uint256 exchangeRate = coinPriceOracle.price(); + uint256 exchangeRate = coinPrice.price(); if (exchangeRate > MAX_RATE) { exchangeRate = MAX_RATE; } - int256 supplyDelta = computeSupplyDelta(exchangeRate, targetRate); + int256 supplyDelta = computeSupplyDelta(exchangeRate, 1); // Apply the Dampening factor. supplyDelta = supplyDelta.div(rebaseLag.toInt256Safe()); @@ -170,12 +172,12 @@ contract MonetaryPolicy { } constructor() public { - owner_ = msg.sender + owner_ = msg.sender; } function initialize( - IRebaseToken rebaseToken_, - ) public initializer { + IRebaseToken rebaseToken_ + ) public { // deviationThreshold = 0.05e18 = 5e16 deviationThreshold = 5 * 10**(DECIMALS - 2); diff --git a/Orchestrator.sol b/contracts/Orchestrator.sol similarity index 85% rename from Orchestrator.sol rename to contracts/Orchestrator.sol index 9480d2c..0cff729 100644 --- a/Orchestrator.sol +++ b/contracts/Orchestrator.sol @@ -3,16 +3,17 @@ * - BSC LINK faucet: https://linkfaucet.protofire.io/bsctest */ -pragma solidity ^0.7.6; +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.8; -import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/evm-contracts/src/v0.6/ChainlinkClient.sol"; +import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol"; interface IMonetaryPolicy { - function triggerRebase() external + function triggerRebase() external; } interface ICoinPriceOracle { - function requestPriceData() external returns (bytes32 requestId) + function requestPriceData() external returns (bytes32 requestId); } contract Orchestrator is ChainlinkClient { @@ -28,16 +29,16 @@ contract Orchestrator is ChainlinkClient { uint256 public lastRebaseTimestampSec; /** - * Network: Kovan - * Oracle: Chainlink - 0xAA1DC356dc4B18f30C347798FD5379F3D77ABC5b - * Job ID: Chainlink - 982105d690504c5d9ce374d040c08654 + * Network: Binance Smart Chain Testnet + * Oracle: Chainlink - 0x3b3D60B4a33B8B8c7798F7B3E8964b76FBE1E176 + * Job ID: Chainlink - 76bea30a605846cea6af93dbae70ed39 * Fee: 0.1 LINK */ constructor() public { owner_ = msg.sender; setPublicChainlinkToken(); - oracle = 0xAA1DC356dc4B18f30C347798FD5379F3D77ABC5b; - jobId = "982105d690504c5d9ce374d040c08654"; + oracle = 0x3b3D60B4a33B8B8c7798F7B3E8964b76FBE1E176; + jobId = "76bea30a605846cea6af93dbae70ed39"; fee = 0.1 * 10 ** 18; // 0.1 LINK defaultDuration = 60 * 60 * 24; defaultCoinPriceLead = 60 * 5; @@ -66,7 +67,7 @@ contract Orchestrator is ChainlinkClient { function initialRebaseRequest(uint256 durationInSeconds) external onlyOwner returns (bytes32 requestId) { - Chainlink.Request memory request = buildChainlinkRequest(jobId, this, this.fulfill.selector); + Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfillRebase.selector); if(lastRebaseTimestampSec == 0){ lastRebaseTimestampSec = block.timestamp; } @@ -77,7 +78,7 @@ contract Orchestrator is ChainlinkClient { function rebaseRequest() private returns (bytes32 requestId) { - Chainlink.Request memory request = buildChainlinkRequest(jobId, this, this.fulfillRebase.selector); + Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfillRebase.selector); require(lastRebaseTimestampSec != 0, "lastRebaseTimestampSec not initialized"); lastRebaseTimestampSec = lastRebaseTimestampSec + defaultDuration; request.addUint("until", lastRebaseTimestampSec); @@ -87,7 +88,7 @@ contract Orchestrator is ChainlinkClient { // must not be called before calling rebaseRequest function coinPriceRequest() private returns (bytes32 requestId) { - Chainlink.Request memory request = buildChainlinkRequest(jobId, this, this.fulfillPriceQuery.selector); + Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfillPriceQuery.selector); require(lastRebaseTimestampSec != 0, "lastRebaseTimestampSec not initialized"); // schedules coin price retrieval defaultCoinPriceLead seconds before rebase request.addUint("until", lastRebaseTimestampSec - defaultCoinPriceLead); diff --git a/RebaseToken.sol b/contracts/RebaseToken.sol similarity index 98% rename from RebaseToken.sol rename to contracts/RebaseToken.sol index 9dc7b48..0142b5b 100644 --- a/RebaseToken.sol +++ b/contracts/RebaseToken.sol @@ -1,8 +1,10 @@ -pragma solidity ^0.7.6; +pragma solidity ^0.6.8; +// SPDX-License-Identifier: MIT import "./SafeMath.sol"; import "./SafeMathInt.sol"; + contract RebaseToken { string public name = "ALPHONE"; string public symbol = "ALP"; @@ -45,7 +47,7 @@ contract RebaseToken { _; } - constructor() public { + constructor() public { owner_ = msg.sender; } diff --git a/SafeMath.sol b/contracts/SafeMath.sol similarity index 96% rename from SafeMath.sol rename to contracts/SafeMath.sol index 031ec4a..9dc808c 100644 --- a/SafeMath.sol +++ b/contracts/SafeMath.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.7.6; +pragma solidity ^0.6.8; +// SPDX-License-Identifier: MIT /** * @title SafeMath diff --git a/SafeMathInt.sol b/contracts/SafeMathInt.sol similarity index 96% rename from SafeMathInt.sol rename to contracts/SafeMathInt.sol index 17f8488..bac3743 100644 --- a/SafeMathInt.sol +++ b/contracts/SafeMathInt.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.7.6; +pragma solidity ^0.6.8; +// SPDX-License-Identifier: MIT /** * @title SafeMathInt diff --git a/UInt256Lib.sol b/contracts/UInt256Lib.sol similarity index 86% rename from UInt256Lib.sol rename to contracts/UInt256Lib.sol index ad69afa..4439a03 100644 --- a/UInt256Lib.sol +++ b/contracts/UInt256Lib.sol @@ -1,4 +1,5 @@ -pragma solidity 0.7.6; +pragma solidity 0.6.8; +// SPDX-License-Identifier: MIT /** * @title Various utilities useful for uint256. diff --git a/contracts/test/LinkToken.sol b/contracts/test/LinkToken.sol new file mode 100644 index 0000000..804388a --- /dev/null +++ b/contracts/test/LinkToken.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.6; + +import "@chainlink/token/contracts/v0.6/LinkToken.sol"; diff --git a/contracts/test/MockOracle.sol b/contracts/test/MockOracle.sol new file mode 100644 index 0000000..8c6f884 --- /dev/null +++ b/contracts/test/MockOracle.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.6; + +import "@chainlink/contracts/src/v0.6/LinkTokenReceiver.sol"; +import "@chainlink/contracts/src/v0.6/interfaces/ChainlinkRequestInterface.sol"; +import "@chainlink/contracts/src/v0.6/interfaces/LinkTokenInterface.sol"; +import "@chainlink/contracts/src/v0.6/vendor/SafeMathChainlink.sol"; + +/** + * @title The Chainlink Mock Oracle contract + * @notice Chainlink smart contract developers can use this to test their contracts + */ +contract MockOracle is ChainlinkRequestInterface, LinkTokenReceiver { + using SafeMathChainlink for uint256; + + uint256 constant public EXPIRY_TIME = 5 minutes; + uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; + + struct Request { + address callbackAddr; + bytes4 callbackFunctionId; + } + + LinkTokenInterface internal LinkToken; + mapping(bytes32 => Request) private commitments; + + event OracleRequest( + bytes32 indexed specId, + address requester, + bytes32 requestId, + uint256 payment, + address callbackAddr, + bytes4 callbackFunctionId, + uint256 cancelExpiration, + uint256 dataVersion, + bytes data + ); + + event CancelOracleRequest( + bytes32 indexed requestId + ); + + /** + * @notice Deploy with the address of the LINK token + * @dev Sets the LinkToken address for the imported LinkTokenInterface + * @param _link The address of the LINK token + */ + constructor(address _link) + public + { + LinkToken = LinkTokenInterface(_link); // external but already deployed and unalterable + } + + /** + * @notice Creates the Chainlink request + * @dev Stores the hash of the params as the on-chain commitment for the request. + * Emits OracleRequest event for the Chainlink node to detect. + * @param _sender The sender of the request + * @param _payment The amount of payment given (specified in wei) + * @param _specId The Job Specification ID + * @param _callbackAddress The callback address for the response + * @param _callbackFunctionId The callback function ID for the response + * @param _nonce The nonce sent by the requester + * @param _dataVersion The specified data version + * @param _data The CBOR payload of the request + */ + function oracleRequest( + address _sender, + uint256 _payment, + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunctionId, + uint256 _nonce, + uint256 _dataVersion, + bytes calldata _data + ) + external + override + onlyLINK() + checkCallbackAddress(_callbackAddress) + { + bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); + require(commitments[requestId].callbackAddr == address(0), "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + uint256 expiration = now.add(EXPIRY_TIME); + + commitments[requestId] = Request( + _callbackAddress, + _callbackFunctionId + ); + + emit OracleRequest( + _specId, + _sender, + requestId, + _payment, + _callbackAddress, + _callbackFunctionId, + expiration, + _dataVersion, + _data); + } + + /** + * @notice Called by the Chainlink node to fulfill requests + * @dev Given params must hash back to the commitment stored from `oracleRequest`. + * Will call the callback address' callback function without bubbling up error + * checking in a `require` so that the node can get paid. + * @param _requestId The fulfillment request ID that must match the requester's + * @param _data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest( + bytes32 _requestId, + bytes32 _data + ) + external + isValidRequest(_requestId) + returns (bool) + { + Request memory req = commitments[_requestId]; + delete commitments[_requestId]; + require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas"); + // All updates to the oracle's fulfillment should come before calling the + // callback(addr+functionId) as it is untrusted. + // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern + (bool success, ) = req.callbackAddr.call(abi.encodeWithSelector(req.callbackFunctionId, _requestId, _data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK + * sent for the request back to the requester's address. + * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid + * Emits CancelOracleRequest event. + * @param _requestId The request ID + * @param _payment The amount of payment given (specified in wei) + * @param _expiration The time of the expiration for the request + */ + function cancelOracleRequest( + bytes32 _requestId, + uint256 _payment, + bytes4, + uint256 _expiration + ) + external + override + { + require(commitments[_requestId].callbackAddr != address(0), "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + require(_expiration <= now, "Request is not expired"); + + delete commitments[_requestId]; + emit CancelOracleRequest(_requestId); + + assert(LinkToken.transfer(msg.sender, _payment)); + } + + /** + * @notice Returns the address of the LINK token + * @dev This is the public implementation for chainlinkTokenAddress, which is + * an internal method of the ChainlinkClient contract + */ + function getChainlinkToken() + public + view + override + returns (address) + { + return address(LinkToken); + } + + // MODIFIERS + + /** + * @dev Reverts if request ID does not exist + * @param _requestId The given request ID to check in stored `commitments` + */ + modifier isValidRequest(bytes32 _requestId) { + require(commitments[_requestId].callbackAddr != address(0), "Must have a valid requestId"); + _; + } + + + /** + * @dev Reverts if the callback address is the LINK token + * @param _to The callback address + */ + modifier checkCallbackAddress(address _to) { + require(_to != address(LinkToken), "Cannot callback to LINK"); + _; + } + +} diff --git a/contracts/test/MockV3Aggregator.sol b/contracts/test/MockV3Aggregator.sol new file mode 100644 index 0000000..914af02 --- /dev/null +++ b/contracts/test/MockV3Aggregator.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.6; + +import "@chainlink/contracts/src/v0.6/tests/MockV3Aggregator.sol"; diff --git a/contracts/test/VRFCoordinatorMock.sol b/contracts/test/VRFCoordinatorMock.sol new file mode 100644 index 0000000..127bdf0 --- /dev/null +++ b/contracts/test/VRFCoordinatorMock.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.6; + +import "@chainlink/contracts/src/v0.6/tests/VRFCoordinatorMock.sol"; diff --git a/deploy/00_Deploy_Mocks.js b/deploy/00_Deploy_Mocks.js new file mode 100644 index 0000000..e271abd --- /dev/null +++ b/deploy/00_Deploy_Mocks.js @@ -0,0 +1,38 @@ +module.exports = async ({ + getNamedAccounts, + deployments, + getChainId +}) => { + const DECIMALS = '18' + const INITIAL_PRICE = '200000000000000000000' + const { deploy, log } = deployments + const { deployer } = await getNamedAccounts() + const chainId = await getChainId() + // If we are on a local development network, we need to deploy mocks! + if (chainId == 31337) { + log("Local network detected! Deploying mocks...") + const linkToken = await deploy('LinkToken', { from: deployer, log: true }) + await deploy('EthUsdAggregator', { + contract: 'MockV3Aggregator', + from: deployer, + log: true, + args: [DECIMALS, INITIAL_PRICE] + }) + await deploy('VRFCoordinatorMock', { + from: deployer, + log: true, + args: [linkToken.address] + }) + await deploy('MockOracle', { + from: deployer, + log: true, + args: [linkToken.address] + }) + log("Mocks Deployed!") + log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + log("You are deploying to a local network, you'll need a local network running to interact") + log("Please run `npx hardhat console` to interact with the deployed smart contracts!") + log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + } +} +module.exports.tags = ['all', 'mocks'] diff --git a/deploy/01_Deploy_PriceConsumerV3.js b/deploy/01_Deploy_PriceConsumerV3.js new file mode 100644 index 0000000..81753fc --- /dev/null +++ b/deploy/01_Deploy_PriceConsumerV3.js @@ -0,0 +1,30 @@ +let { networkConfig } = require('../helper-hardhat-config') + +module.exports = async ({ + getNamedAccounts, + deployments, + getChainId +}) => { + const { deploy, log } = deployments + const { deployer } = await getNamedAccounts() + const chainId = await getChainId() + let ethUsdPriceFeedAddress + if (chainId == 31337) { + const EthUsdAggregator = await deployments.get('EthUsdAggregator') + ethUsdPriceFeedAddress = EthUsdAggregator.address + } else { + ethUsdPriceFeedAddress = networkConfig[chainId]['ethUsdPriceFeed'] + } + // Price Feed Address, values can be obtained at https://docs.chain.link/docs/reference-contracts + // Default one below is ETH/USD contract on Kovan + const priceConsumerV3 = await deploy('PriceConsumerV3', { + from: deployer, + args: [ethUsdPriceFeedAddress], + log: true + }) + log("Run Price Feed contract with command:") + log("npx hardhat read-price-feed --contract " + priceConsumerV3.address + " --network " + networkConfig[chainId]['name']) + log("----------------------------------------------------") +} + +module.exports.tags = ['all', 'feed'] diff --git a/deploy/02_Deploy_APIConsumer.js b/deploy/02_Deploy_APIConsumer.js new file mode 100644 index 0000000..1aa6428 --- /dev/null +++ b/deploy/02_Deploy_APIConsumer.js @@ -0,0 +1,40 @@ +let { networkConfig } = require('../helper-hardhat-config') + +module.exports = async ({ + getNamedAccounts, + deployments +}) => { + const { deploy, log, get } = deployments + const { deployer } = await getNamedAccounts() + const chainId = await getChainId() + let linkTokenAddress + let oracle + let additionalMessage = "" + + if (chainId == 31337) { + linkToken = await get('LinkToken') + MockOracle = await get('MockOracle') + linkTokenAddress = linkToken.address + oracle = MockOracle.address + additionalMessage = " --linkaddress " + linkTokenAddress + } else { + linkTokenAddress = networkConfig[chainId]['linkToken'] + oracle = networkConfig[chainId]['oracle'] + } + const jobId = networkConfig[chainId]['jobId'] + const fee = networkConfig[chainId]['fee'] + + const apiConsumer = await deploy('APIConsumer', { + from: deployer, + args: [oracle, jobId, fee, linkTokenAddress], + log: true + }) + + log("Run the following command to fund contract with LINK:") + log("npx hardhat fund-link --contract " + apiConsumer.address + " --network " + networkConfig[chainId]['name'] + additionalMessage) + log("Then run API Consumer contract with following command:") + log("npx hardhat request-data --contract " + apiConsumer.address + " --network " + networkConfig[chainId]['name']) + log("----------------------------------------------------") +} + +module.exports.tags = ['all', 'api'] diff --git a/deploy/03_Deploy_RandomNumberConsumer.js b/deploy/03_Deploy_RandomNumberConsumer.js new file mode 100644 index 0000000..9ae5342 --- /dev/null +++ b/deploy/03_Deploy_RandomNumberConsumer.js @@ -0,0 +1,42 @@ +let { networkConfig } = require('../helper-hardhat-config') + + +module.exports = async ({ + getNamedAccounts, + deployments, + getChainId +}) => { + const { deploy, get, log } = deployments + const { deployer } = await getNamedAccounts() + const chainId = await getChainId() + let linkTokenAddress + let vrfCoordinatorAddress + let additionalMessage = "" + + if (chainId == 31337) { + linkToken = await get('LinkToken') + VRFCoordinatorMock = await get('VRFCoordinatorMock') + linkTokenAddress = linkToken.address + vrfCoordinatorAddress = VRFCoordinatorMock.address + additionalMessage = " --linkaddress " + linkTokenAddress + } else { + linkTokenAddress = networkConfig[chainId]['linkToken'] + vrfCoordinatorAddress = networkConfig[chainId]['vrfCoordinator'] + } + const keyHash = networkConfig[chainId]['keyHash'] + const fee = networkConfig[chainId]['fee'] + + const randomNumberConsumer = await deploy('RandomNumberConsumer', { + from: deployer, + args: [vrfCoordinatorAddress, linkTokenAddress, keyHash, fee], + log: true + }) + + log("Run the following command to fund contract with LINK:") + log("npx hardhat fund-link --contract " + randomNumberConsumer.address + " --network " + networkConfig[chainId]['name'] + additionalMessage) + log("Then run RandomNumberConsumer contract with the following command, replacing '777' with your chosen seed number:") + log("npx hardhat request-random-number --contract " + randomNumberConsumer.address, " --seed '777'" + " --network " + networkConfig[chainId]['name']) + log("----------------------------------------------------") +} + +module.exports.tags = ['all', 'vrf'] diff --git a/hardhat.config.js b/hardhat.config.js new file mode 100644 index 0000000..cc9a7cd --- /dev/null +++ b/hardhat.config.js @@ -0,0 +1,96 @@ +/** + * @type import('hardhat/config').HardhatUserConfig + */ +require("@nomiclabs/hardhat-waffle"); +require("@nomiclabs/hardhat-ethers"); +require("@nomiclabs/hardhat-truffle5"); +require("@nomiclabs/hardhat-etherscan"); +require("hardhat-deploy"); +require("./tasks/accounts"); +require("./tasks/balance"); +require("./tasks/fund-link"); +require("./tasks/block-number"); +require("./tasks/block-number"); +require("./tasks/random-number-consumer"); +require("./tasks/price-consumer"); +require("./tasks/api-consumer"); + +require("dotenv").config(); + +const MAINNET_RPC_URL = + process.env.MAINNET_RPC_URL || + process.env.ALCHEMY_MAINNET_RPC_URL || + "https://eth-mainnet.alchemyapi.io/v2/your-api-key"; +const RINKEBY_RPC_URL = + process.env.RINKEBY_RPC_URL || + "https://eth-rinkeby.alchemyapi.io/v2/your-api-key"; +const KOVAN_RPC_URL = + process.env.KOVAN_RPC_URL || + "https://eth-kovan.alchemyapi.io/v2/your-api-key"; +const MNEMONIC = process.env.MNEMONIC || "your mnemonic"; +const ETHERSCAN_API_KEY = + process.env.ETHERSCAN_API_KEY || "Your etherscan API key"; +// optional +const PRIVATE_KEY = process.env.PRIVATE_KEY || "your private key"; + +module.exports = { + defaultNetwork: "hardhat", + networks: { + hardhat: { + // // If you want to do some forking, uncomment this + // forking: { + // url: MAINNET_RPC_URL + // } + }, + localhost: {}, + kovan: { + url: KOVAN_RPC_URL, + // accounts: [PRIVATE_KEY], + accounts: { + mnemonic: MNEMONIC, + }, + saveDeployments: true, + }, + rinkeby: { + url: RINKEBY_RPC_URL, + // accounts: [PRIVATE_KEY], + accounts: { + mnemonic: MNEMONIC, + }, + saveDeployments: true, + }, + ganache: { + url: "http://localhost:8545", + accounts: { + mnemonic: MNEMONIC, + }, + }, + }, + etherscan: { + // Your API key for Etherscan + // Obtain one at https://etherscan.io/ + apiKey: ETHERSCAN_API_KEY, + }, + namedAccounts: { + deployer: { + default: 0, // here this will by default take the first account as deployer + 1: 0, // similarly on mainnet it will take the first account as deployer. Note though that depending on how hardhat network are configured, the account 0 on one network can be different than on another + }, + feeCollector: { + default: 1, + }, + }, + solidity: { + compilers: [ + { + version: "0.6.8", + }, + { + version: "0.6.6", + }, + ], + }, + mocha: { + timeout: 100000, + }, +}; diff --git a/helper-hardhat-config.js b/helper-hardhat-config.js new file mode 100644 index 0000000..c41b980 --- /dev/null +++ b/helper-hardhat-config.js @@ -0,0 +1,57 @@ +const networkConfig = { + default: { + name: 'hardhat', + fee: '100000000000000000', + keyHash: '0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4', + jobId: '29fa9aa13bf1468788b7cc4a500a45b8' + }, + 31337: { + name: 'localhost', + fee: '100000000000000000', + keyHash: '0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4', + jobId: '29fa9aa13bf1468788b7cc4a500a45b8' + }, + 42: { + name: 'kovan', + linkToken: '0xa36085F69e2889c224210F603D836748e7dC0088', + ethUsdPriceFeed: '0x9326BFA02ADD2366b30bacB125260Af641031331', + keyHash: '0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4', + vrfCoordinator: '0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9', + oracle: '0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e', + jobId: '29fa9aa13bf1468788b7cc4a500a45b8', + fee: '100000000000000000', + }, + 4: { + name: 'rinkeby', + linkToken: '0x01be23585060835e02b77ef475b0cc51aa1e0709', + ethUsdPriceFeed: '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e', + keyHash: '0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311', + vrfCoordinator: '0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B', + oracle: '0x7AFe1118Ea78C1eae84ca8feE5C65Bc76CcF879e', + jobId: '6d1bfe27e7034b1d87b5270556b17277', + fee: '100000000000000000', + }, + 1: { + name: 'mainnet', + linkToken: '0x514910771af9ca656af840dff83e8264ecf986ca' + }, + 5: { + name: 'goerli', + linkToken: '0x326c977e6efc84e512bb9c30f76e30c160ed06fb' + } +} + +const getNetworkIdFromName = async (networkIdName) => { + for (const id in networkConfig) { + if (networkConfig[id]['name'] == networkIdName) { + return id + } + } + return null +} + +module.exports = { + networkConfig, + getNetworkIdFromName +} + diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3fb5ca6946de0bc823d3fd3e6ce1dac6b7e244cf GIT binary patch literal 2884 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 i;PZ@H4;`TYxBU;%JVs&fCx@p10000`. +// +// When running the script with `hardhat run