diff --git a/.gitmodules b/.gitmodules index cb79e36..0369204 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/smartcontractkit/chainlink-brownie-contracts [submodule "lib/solmate"] path = lib/solmate - url = https://github.com/rari-capital/solmate + url = https://github.com/transmissions11/solmate [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 47cab42..0000000 --- a/.prettierignore +++ /dev/null @@ -1,7 +0,0 @@ -build -coverage -out -lib -assets -node_modules -.next \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 87cf9d6..0000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "trailingComma": "es5", - "tabWidth": 4, - "semi": false, - "singleQuote": false -} diff --git a/.solhint.json b/.solhint.json deleted file mode 100644 index 10945a7..0000000 --- a/.solhint.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "solhint:recommended", - "rules": { - "code-complexity": ["error", 8], - "compiler-version": ["error", ">=0.5.8"], - "const-name-snakecase": "off", - "constructor-syntax": "error", - "func-visibility": ["error", { "ignoreConstructors": true }], - "max-line-length": ["error", 120], - "not-rely-on-time": "off", - "prettier/prettier": [ - "error", - { - "endOfLine": "auto" - } - ], - "reason-string": ["warn", { "maxLength": 64 }] - } -} diff --git a/Makefile b/Makefile deleted file mode 100644 index a464f3a..0000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ --include .env - -.PHONY: all test clean deploy-anvil - -all: clean remove install update build - -# Clean the repo -clean :; forge clean - -# Remove modules -remove :; rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib && touch .gitmodules && git add . && git commit -m "modules" - -install :; forge install smartcontractkit/chainlink-brownie-contracts && forge install rari-capital/solmate && forge install foundry-rs/forge-std - -# Update Dependencies -update:; forge update - -build:; forge build - -test :; forge test - -snapshot :; forge snapshot - -slither :; slither ./src - -format :; prettier --write src/**/*.sol && prettier --write src/*.sol - -# solhint should be installed globally -lint :; solhint src/**/*.sol && solhint src/*.sol - -anvil :; anvil -m 'test test test test test test test test test test test junk' - -# use the "@" to hide the command from your shell -deploy-sepolia :; @forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${SEPOLIA_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast --verify --etherscan-api-key ${ETHERSCAN_API_KEY} -vvvv - -# This is the private key of account from the mnemonic from the "make anvil" command -deploy-anvil :; @forge script script/${contract}.s.sol:Deploy${contract} --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast - -deploy-all :; make deploy-${network} contract=APIConsumer && make deploy-${network} contract=KeepersCounter && make deploy-${network} contract=PriceFeedConsumer && make deploy-${network} contract=VRFConsumerV2 - --include ${FCT_PLUGIN_PATH}/makefile-external diff --git a/README.md b/README.md index 42953a3..dbea15e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -*Note: This repo has been recently updated for Sepolia* +_Note: This repo has been recently updated for Sepolia_ # Foundry Starter Kit @@ -14,7 +14,6 @@ Foundry Starter Kit is a repo that shows developers how to quickly build, test, and deploy smart contracts with one of the fastest frameworks out there, [foundry](https://github.com/gakonst/foundry)! - - [Foundry Starter Kit](#foundry-starter-kit) - [Getting Started](#getting-started) - [Requirements](#requirements) @@ -37,45 +36,46 @@ Foundry Starter Kit is a repo that shows developers how to quickly build, test, Please install the following: -- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - - You'll know you've done it right if you can run `git --version` -- [Foundry / Foundryup](https://github.com/gakonst/foundry) - - This will install `forge`, `cast`, and `anvil` - - You can test you've installed them right by running `forge --version` and get an output like: `forge 0.2.0 (f016135 2022-07-04T00:15:02.930499Z)` - - To get the latest of each, just run `foundryup` - -And you probably already have `make` installed... but if not [try looking here.](https://askubuntu.com/questions/161104/how-do-i-install-make) +- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + - You'll know you've done it right if you can run `git --version` +- [Foundry / Foundryup](https://github.com/gakonst/foundry) + - This will install `forge`, `cast`, and `anvil` + - You can test you've installed them right by running `forge --version` and get an output like: `forge 0.2.0 (f016135 2022-07-04T00:15:02.930499Z)` + - To get the latest of each, just run `foundryup` ## Quickstart ```sh git clone https://github.com/smartcontractkit/foundry-starter-kit cd foundry-starter-kit -make # This installs the project's dependencies. -make test ``` -## Testing +## Install dependencies as follows: -``` -make test -``` +Run `forge install` to install dependencies. [Foundry uses git submodules](https://book.getfoundry.sh/projects/dependencies) as its dependency management system. -or +> ⚠️ when running forge install, you may see an error message if you have uncomitted changes in your repo. Read the message carefully - it may inform you that you can add the `--no-commit` flag to each of these `install` commands if your workspace has uncommitted changes. +You can update dependencies by running `forge update` + +## Testing +To check that everything is compiling and working as intended after cloning and installing dependencies, run ``` forge test ``` +All tests should pass. + # Chainlink Foundry Starter Kit - Implementation of the following 4 Chainlink services using the [Foundry] (https://book.getfoundry.sh/) smart contract development tooling: - - [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts) - - [Chainlink VRF V2](https://docs.chain.link/docs/chainlink-vrf) - - [Chainlink Automation](https://docs.chain.link/chainlink-automation/introduction) - - [Request & Receive data (AnyAPI)](https://docs.chain.link/any-api/introduction) +Implementation of the following 4 Chainlink services using the [Foundry] (https://book.getfoundry.sh/) smart contract development tooling: -For [Chainlink Functions](https://docs.chain.link/chainlink-functions) please go to these tarter kits: [Hardhat](https://github.com/smartcontractkit/functions-hardhat-starter-kit) | [Foundry (coming soon)](https://github.com/smartcontractkit/functions-foundry-starter-kit) +- [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts) +- [Chainlink VRF V2](https://docs.chain.link/docs/chainlink-vrf) +- [Chainlink Automation](https://docs.chain.link/chainlink-automation/introduction) +- [Request & Receive data (AnyAPI)](https://docs.chain.link/any-api/introduction) + +For [Chainlink Functions](https://docs.chain.link/chainlink-functions) please go to these starter kits: [Hardhat](https://github.com/smartcontractkit/functions-hardhat-starter-kit) | [Foundry (coming soon)](https://github.com/smartcontractkit/functions-foundry-starter-kit) For [Chainlink CCIP (Cross Chain Interoperability Prototocol)](https://docs.chain.link/ccip) please go to these starter kits: [Hardhat](https://github.com/smartcontractkit/ccip-starter-kit-hardhat) | [Foundry](https://github.com/smartcontractkit/ccip-starter-kit-foundry) @@ -89,63 +89,58 @@ We'll demo using the Sepolia testnet. (Go here for [testnet sepolia ETH](https:/ You'll need to add the following variables to a `.env` file: -- `SEPOLIA_RPC_URL`: A URL to connect to the blockchain. You can get one for free from [Infura](https://www.infura.io/) account -- `PRIVATE_KEY`: A private key from your wallet. You can get a private key from a new [Metamask](https://metamask.io/) account - - Additionally, if you want to deploy to a testnet, you'll need test ETH and/or LINK. You can get them from [faucets.chain.link](https://faucets.chain.link/). -- Optional `ETHERSCAN_API_KEY`: If you want to verify on etherscan +- `SEPOLIA_RPC_URL`: A URL to connect to the blockchain. You can get one for free from [Infura](https://www.infura.io/) account +- `PRIVATE_KEY`: A private key from your wallet. You can get a private key from a new [Metamask](https://metamask.io/) account + - Additionally, if you want to deploy to a testnet, you'll need test ETH and/or LINK. You can get them from [faucets.chain.link](https://faucets.chain.link/). +- Optional `ETHERSCAN_API_KEY`: If you want to verify on etherscan + +When you've added your environment variables to the `.env` file, run `source .env` in your terminal (and for each new terminal session) to load the environment variables into your terminal. ## Deploying -``` -make deploy-sepolia contract= -``` +Deploy scripts are in `./script`. The relevant Chainlink Service can be determined from the name of the Contract script. `HelperConfig` is not meant to be deployed. -For example: +To deploy one of the Chainlink Service consumer contracts run the script as follows: ``` -make deploy-sepolia contract=PriceFeedConsumer +forge script script/${CONTRACT_NAME}.s.sol:Deploy${CONTRACT_NAME} --rpc-url $SEPOLIA_RPC_URL --private-key PRIVATE_KEY --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY -vvvv +make deploy-sepolia contract= ``` -This will run the forge script, the script it's running is: +For example, to deploy the `PriceFeedConsumer` contract: ``` -@forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${SEPOLIA_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast --verify --etherscan-api-key ${ETHERSCAN_API_KEY} -vvvv +forge script script/PriceFeedConsumer.s.sol:DeployPriceFeedConsumer --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY -vvvv ``` -If you don't have an `ETHERSCAN_API_KEY`, you can also just run: +If you don't have an `ETHERSCAN_API_KEY`, you can omit `--verify --etherscan-api-key $ETHERSCAN_API_KEY` -``` -@forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${SEPOLIA_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast -``` - -These pull from the files in the `script` folder. -### Working with a local network +### Working with Anvil local development network -Foundry comes with local network [anvil](https://book.getfoundry.sh/anvil/index.html) baked in, and allows us to deploy to our local network for quick testing locally. +Foundry comes with local network [anvil](https://book.getfoundry.sh/anvil/index.html) baked in, and allows us to deploy to our local network for quick testing locally. -To start a local network run: +To start a local network run the following in a new terminal window or tab: ``` -make anvil +anvil ``` -This will spin up a local blockchain with a determined private key, so you can use the same private key each time. +This will spin up a local blockchain on `http://localhost:8545` : (see console output for the mnemonic used, and 10 private keys and their associated wallet address), so you can use the same private key each time. -Then, you can deploy to it with: +Then, you can deploy to it with one of those private keys; in this example we use the first one: ``` -make deploy-anvil contract= +forge script script/${contract}.s.sol:Deploy${contract} --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast ``` -Similar to `deploy-sepolia` ### Working with other chains -To add a chain, you'd just need to make a new entry in the `Makefile`, and replace `` with whatever your chain's information is. +To add a chain, you'd just need to pass in the RPC URL for the relevant chain to the `--rpc-url` flag. ``` -deploy- :; @forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast -vvvv +forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast -vvvv ``` @@ -159,20 +154,19 @@ Then, you can run: make slither ``` -And get your slither output. - - +And get your slither output. # Contributing Contributions are always welcome! Open a PR or an issue! +If you do contribute please add `solidity.formatter": "forge` to your VSCode Settings, or run `forge fmt` before you commit and push. # Thank You! ## Resources -- [Chainlink Documentation](https://docs.chain.link/) -- [Foundry Documentation](https://book.getfoundry.sh/) +- [Chainlink Documentation](https://docs.chain.link/) +- [Foundry Documentation](https://book.getfoundry.sh/) ### TODO diff --git a/lib/forge-std b/lib/forge-std index 2cbff06..1714bee 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 2cbff0602d340503dba9828ab6981053704d1384 +Subproject commit 1714bee72e286e73f76e320d110e0eaf5c4e649d diff --git a/lib/foundry-chainlink-toolkit b/lib/foundry-chainlink-toolkit index 6bd2079..caa68b1 160000 --- a/lib/foundry-chainlink-toolkit +++ b/lib/foundry-chainlink-toolkit @@ -1 +1 @@ -Subproject commit 6bd2079c85d7ec020e2e3b52cbdb69163ce5c75c +Subproject commit caa68b15a67340b13f4d1a2147a7c454b3443744 diff --git a/package.json b/package.json deleted file mode 100644 index f94dd6b..0000000 --- a/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "foundry-starter-kit", - "version": "1.0.0", - "description": "Minimal Template for Foundry Projects", - "author": "Patrick Collins", - "license": "MIT", - "files": [ - "src/**/*.sol" - ], - "devDependencies": { - "prettier": "^2.7.1", - "prettier-plugin-solidity": "^1.0.0-beta.19" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/smartcontractkit/foundry-starter-kit.git" - }, - "bugs": { - "url": "https://github.com/smartcontractkit/foundry-starter-kit/issues" - }, - "scripts": { - "setup": "make clean && make build", - "sync": "make update", - "test": "make test", - "snapshot": "make snapshot", - "format": "make format", - "lint": "make lint", - "all": "make all" - } -} diff --git a/script/APIConsumer.s.sol b/script/APIConsumer.s.sol index 35ad3da..756bf15 100644 --- a/script/APIConsumer.s.sol +++ b/script/APIConsumer.s.sol @@ -4,24 +4,14 @@ pragma solidity ^0.8.7; import "forge-std/Script.sol"; import "../src/APIConsumer.sol"; import "./HelperConfig.sol"; -import "../src/test/mocks/MockOracle.sol"; -import "../src/test/mocks/LinkToken.sol"; +import "../test/mocks/MockOracle.sol"; +import "../test//mocks/LinkToken.sol"; contract DeployAPIConsumer is Script, HelperConfig { function run() external { HelperConfig helperConfig = new HelperConfig(); - ( - address oracle, - bytes32 jobId, - uint256 chainlinkFee, - address link, - , - , - , - , - - ) = helperConfig.activeNetworkConfig(); + (address oracle, bytes32 jobId, uint256 chainlinkFee, address link,,,,,) = helperConfig.activeNetworkConfig(); if (link == address(0)) { link = address(new LinkToken()); diff --git a/script/HelperConfig.sol b/script/HelperConfig.sol index 5c345c5..70bdfa5 100644 --- a/script/HelperConfig.sol +++ b/script/HelperConfig.sol @@ -25,11 +25,7 @@ contract HelperConfig { activeNetworkConfig = chainIdToNetworkConfig[block.chainid]; } - function getSepoliaEthConfig() - internal - pure - returns (NetworkConfig memory sepoliaNetworkConfig) - { + function getSepoliaEthConfig() internal pure returns (NetworkConfig memory sepoliaNetworkConfig) { sepoliaNetworkConfig = NetworkConfig({ oracle: 0x6090149792dAAeE9D1D568c9f9a6F6B46AA29eFD, jobId: "ca98366cc7314957b8c012c72f05aeeb", @@ -43,11 +39,7 @@ contract HelperConfig { }); } - function getAnvilEthConfig() - internal - pure - returns (NetworkConfig memory anvilNetworkConfig) - { + function getAnvilEthConfig() internal pure returns (NetworkConfig memory anvilNetworkConfig) { anvilNetworkConfig = NetworkConfig({ oracle: address(0), // This is a mock jobId: "6b88e0402e5d415eb946e528b8e0c7ba", diff --git a/script/KeepersCounter.s.sol b/script/KeepersCounter.s.sol index b3c189c..0ee7573 100644 --- a/script/KeepersCounter.s.sol +++ b/script/KeepersCounter.s.sol @@ -9,8 +9,7 @@ contract DeployKeepersCounter is Script, HelperConfig { function run() external { HelperConfig helperConfig = new HelperConfig(); - (, , , , uint256 updateInterval, , , , ) = helperConfig - .activeNetworkConfig(); + (,,,, uint256 updateInterval,,,,) = helperConfig.activeNetworkConfig(); vm.startBroadcast(); diff --git a/script/PriceFeedConsumer.s.sol b/script/PriceFeedConsumer.s.sol index 4543dc7..36ca80d 100644 --- a/script/PriceFeedConsumer.s.sol +++ b/script/PriceFeedConsumer.s.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.7; import "forge-std/Script.sol"; import "../src/PriceFeedConsumer.sol"; import "./HelperConfig.sol"; -import "../src/test/mocks/MockV3Aggregator.sol"; +import "../test/mocks/MockV3Aggregator.sol"; contract DeployPriceFeedConsumer is Script, HelperConfig { uint8 constant DECIMALS = 18; @@ -13,8 +13,7 @@ contract DeployPriceFeedConsumer is Script, HelperConfig { function run() external { HelperConfig helperConfig = new HelperConfig(); - (, , , , , address priceFeed, , , ) = helperConfig - .activeNetworkConfig(); + (,,,,, address priceFeed,,,) = helperConfig.activeNetworkConfig(); if (priceFeed == address(0)) { priceFeed = address(new MockV3Aggregator(DECIMALS, INITIAL_ANSWER)); diff --git a/script/VRFConsumerV2.s.sol b/script/VRFConsumerV2.s.sol index dd1e1a5..77343a3 100644 --- a/script/VRFConsumerV2.s.sol +++ b/script/VRFConsumerV2.s.sol @@ -4,24 +4,15 @@ pragma solidity ^0.8.7; import "forge-std/Script.sol"; import "../src/VRFConsumerV2.sol"; import "./HelperConfig.sol"; -import "../src/test/mocks/LinkToken.sol"; -import "../src/test/mocks/MockVRFCoordinatorV2.sol"; +import "../test/mocks/LinkToken.sol"; +import "../test/mocks/MockVRFCoordinatorV2.sol"; contract DeployVRFConsumerV2 is Script, HelperConfig { function run() external { HelperConfig helperConfig = new HelperConfig(); - ( - , - , - , - address link, - , - , - uint64 subscriptionId, - address vrfCoordinator, - bytes32 keyHash - ) = helperConfig.activeNetworkConfig(); + (,,, address link,,, uint64 subscriptionId, address vrfCoordinator, bytes32 keyHash) = + helperConfig.activeNetworkConfig(); if (link == address(0)) { link = address(new LinkToken()); @@ -33,13 +24,8 @@ contract DeployVRFConsumerV2 is Script, HelperConfig { vm.startBroadcast(); - new VRFConsumerV2( - subscriptionId, - vrfCoordinator, - link, - keyHash - ); - + new VRFConsumerV2(subscriptionId, vrfCoordinator, link, keyHash); + vm.stopBroadcast(); } } diff --git a/src/APIConsumer.sol b/src/APIConsumer.sol index ec64a5e..b87dc5d 100644 --- a/src/APIConsumer.sol +++ b/src/APIConsumer.sol @@ -17,12 +17,7 @@ contract APIConsumer is ChainlinkClient { event DataFullfilled(uint256 volume); - constructor( - address _oracle, - bytes32 _jobId, - uint256 _fee, - address _link - ) { + constructor(address _oracle, bytes32 _jobId, uint256 _fee, address _link) { if (_link == address(0)) { _setPublicChainlinkToken(); } else { @@ -40,17 +35,10 @@ contract APIConsumer is ChainlinkClient { * @return requestId - id of the request */ function requestVolumeData() public returns (bytes32 requestId) { - Chainlink.Request memory request = _buildChainlinkRequest( - jobId, - address(this), - this.fulfill.selector - ); + 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" - ); + 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": @@ -67,7 +55,7 @@ contract APIConsumer is ChainlinkClient { request._add("path", "RAW,ETH,USD,VOLUME24HOUR"); // Multiply the result by 1000000000000000000 to remove decimals - int256 timesAmount = 10**18; + int256 timesAmount = 10 ** 18; request._addInt("times", timesAmount); // Sends the request @@ -80,10 +68,7 @@ contract APIConsumer is ChainlinkClient { * @param _requestId - id of the request * @param _volume - response; requested 24h trading volume of ETH in USD */ - function fulfill(bytes32 _requestId, uint256 _volume) - public - recordChainlinkFulfillment(_requestId) - { + function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId) { volume = _volume; emit DataFullfilled(_volume); } diff --git a/src/KeepersCounter.sol b/src/KeepersCounter.sol index cbda49b..643cf77 100644 --- a/src/KeepersCounter.sol +++ b/src/KeepersCounter.sol @@ -14,9 +14,7 @@ contract KeepersCounter is KeeperCompatibleInterface { counter = 0; } - function checkUpkeep( - bytes memory /* checkData */ - ) + function checkUpkeep(bytes memory /* checkData */ ) public view override @@ -26,11 +24,9 @@ contract KeepersCounter is KeeperCompatibleInterface { performData = bytes(""); } - function performUpkeep( - bytes calldata /* performData */ - ) external override { + function performUpkeep(bytes calldata /* performData */ ) external override { // add some verification - (bool upkeepNeeded, ) = checkUpkeep(""); + (bool upkeepNeeded,) = checkUpkeep(""); require(upkeepNeeded, "Time interval not met"); lastTimeStamp = block.timestamp; diff --git a/src/PriceFeedConsumer.sol b/src/PriceFeedConsumer.sol index 7ac48fc..ee89bbf 100644 --- a/src/PriceFeedConsumer.sol +++ b/src/PriceFeedConsumer.sol @@ -26,11 +26,10 @@ contract PriceFeedConsumer { int256 price, , , - ) = /* uint256 startedAt */ - /* uint256 timeStamp */ - /* uint80 answeredInRound */ - priceFeed.latestRoundData(); + /* uint256 timeStamp */ + /* uint80 answeredInRound */ + priceFeed.latestRoundData(); return price; } diff --git a/src/VRFConsumerV2.sol b/src/VRFConsumerV2.sol index b019e3c..70b6ad2 100644 --- a/src/VRFConsumerV2.sol +++ b/src/VRFConsumerV2.sol @@ -50,12 +50,9 @@ contract VRFConsumerV2 is VRFConsumerBaseV2 { * @param vrfCoordinator - coordinator, check https://docs.chain.link/docs/vrf-contracts/#configurations * @param keyHash - the gas lane to use, which specifies the maximum gas price to bump to */ - constructor( - uint64 subscriptionId, - address vrfCoordinator, - address link, - bytes32 keyHash - ) VRFConsumerBaseV2(vrfCoordinator) { + constructor(uint64 subscriptionId, address vrfCoordinator, address link, bytes32 keyHash) + VRFConsumerBaseV2(vrfCoordinator) + { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link); s_keyHash = keyHash; @@ -70,11 +67,7 @@ contract VRFConsumerV2 is VRFConsumerBaseV2 { function requestRandomWords() external onlyOwner { // Will revert if subscription is not set and funded. s_requestId = COORDINATOR.requestRandomWords( - s_keyHash, - s_subscriptionId, - s_requestConfirmations, - s_callbackGasLimit, - s_numWords + s_keyHash, s_subscriptionId, s_requestConfirmations, s_callbackGasLimit, s_numWords ); } @@ -84,10 +77,7 @@ contract VRFConsumerV2 is VRFConsumerBaseV2 { * @param requestId - id of the request * @param randomWords - array of random results from VRF Coordinator */ - function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) - internal - override - { + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { s_randomWords = randomWords; emit ReturnedRandomness(randomWords); } diff --git a/src/test/APIConsumer.t.sol b/test/APIConsumer.t.sol similarity index 81% rename from src/test/APIConsumer.t.sol rename to test/APIConsumer.t.sol index 911c731..7a29343 100644 --- a/src/test/APIConsumer.t.sol +++ b/test/APIConsumer.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "../APIConsumer.sol"; +import "../src/APIConsumer.sol"; import "./mocks/LinkToken.sol"; import "forge-std/Test.sol"; import "./mocks/MockOracle.sol"; @@ -16,18 +16,13 @@ contract APIConsumerTest is Test { uint256 fee; bytes32 blank_bytes32; - uint256 constant AMOUNT = 1 * 10**18; + uint256 constant AMOUNT = 1 * 10 ** 18; uint256 constant RESPONSE = 777; function setUp() public { linkToken = new LinkToken(); mockOracle = new MockOracle(address(linkToken)); - apiConsumer = new APIConsumer( - address(mockOracle), - jobId, - fee, - address(linkToken) - ); + apiConsumer = new APIConsumer(address(mockOracle), jobId, fee, address(linkToken)); linkToken.transfer(address(apiConsumer), AMOUNT); } diff --git a/src/test/KeepersCounter.t.sol b/test/KeepersCounter.t.sol similarity index 90% rename from src/test/KeepersCounter.t.sol rename to test/KeepersCounter.t.sol index 67624ca..c4188fd 100644 --- a/src/test/KeepersCounter.t.sol +++ b/test/KeepersCounter.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "../KeepersCounter.sol"; +import "../src/KeepersCounter.sol"; import "forge-std/Test.sol"; import "./utils/Cheats.sol"; @@ -19,13 +19,13 @@ contract KeepersCounterTest is Test { } function testCheckupReturnsFalseBeforeTime() public { - (bool upkeepNeeded, ) = counter.checkUpkeep("0x"); + (bool upkeepNeeded,) = counter.checkUpkeep("0x"); assertTrue(!upkeepNeeded); } function testCheckupReturnsTrueAfterTime() public { cheats.warp(staticTime + INTERVAL + 1); // Needs to be more than the interval - (bool upkeepNeeded, ) = counter.checkUpkeep("0x"); + (bool upkeepNeeded,) = counter.checkUpkeep("0x"); assertTrue(upkeepNeeded); } diff --git a/src/test/PriceFeedConsumer.t.sol b/test/PriceFeedConsumer.t.sol similarity index 87% rename from src/test/PriceFeedConsumer.t.sol rename to test/PriceFeedConsumer.t.sol index 1640f25..c8f2fee 100644 --- a/src/test/PriceFeedConsumer.t.sol +++ b/test/PriceFeedConsumer.t.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.0; -import "../PriceFeedConsumer.sol"; +import "../src/PriceFeedConsumer.sol"; import "./mocks/MockV3Aggregator.sol"; import "forge-std/Test.sol"; contract PriceFeedConsumerTest is Test { uint8 public constant DECIMALS = 18; - int256 public constant INITIAL_ANSWER = 1 * 10**18; + int256 public constant INITIAL_ANSWER = 1 * 10 ** 18; PriceFeedConsumer public priceFeedConsumer; MockV3Aggregator public mockV3Aggregator; diff --git a/src/test/VRFConsumerV2.t.sol b/test/VRFConsumerV2.t.sol similarity index 84% rename from src/test/VRFConsumerV2.t.sol rename to test/VRFConsumerV2.t.sol index 14811ea..e22ddc8 100644 --- a/src/test/VRFConsumerV2.t.sol +++ b/test/VRFConsumerV2.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../VRFConsumerV2.sol"; +import "../src/VRFConsumerV2.sol"; import "./mocks/MockVRFCoordinatorV2.sol"; import "./mocks/LinkToken.sol"; import "./utils/Cheats.sol"; @@ -13,7 +13,7 @@ contract VRFConsumerV2Test is Test { VRFConsumerV2 public vrfConsumer; Cheats internal constant cheats = Cheats(VM_ADDRESS); - uint96 constant FUND_AMOUNT = 1 * 10**18; + uint96 constant FUND_AMOUNT = 1 * 10 ** 18; // Initialized as blank, fine for testing uint64 subId; @@ -26,12 +26,7 @@ contract VRFConsumerV2Test is Test { vrfCoordinator = new MockVRFCoordinatorV2(); subId = vrfCoordinator.createSubscription(); vrfCoordinator.fundSubscription(subId, FUND_AMOUNT); - vrfConsumer = new VRFConsumerV2( - subId, - address(vrfCoordinator), - address(linkToken), - keyHash - ); + vrfConsumer = new VRFConsumerV2(subId, address(vrfCoordinator), address(linkToken), keyHash); vrfCoordinator.addConsumer(subId, address(vrfConsumer)); } @@ -62,15 +57,11 @@ contract VRFConsumerV2Test is Test { cheats.expectEmit(false, false, false, true); emit ReturnedRandomness(words); // When testing locally you MUST call fulfillRandomness youself to get the - // randomness to the consumer contract, since there isn't a chainlink node on your local network + // randomness to the consumer contract, since there isn't a chainlink node on your local network vrfCoordinator.fulfillRandomWords(requestId, address(vrfConsumer)); } - function getWords(uint256 requestId) - public - view - returns (uint256[] memory) - { + function getWords(uint256 requestId) public view returns (uint256[] memory) { uint256[] memory words = new uint256[](vrfConsumer.s_numWords()); for (uint256 i = 0; i < vrfConsumer.s_numWords(); i++) { words[i] = uint256(keccak256(abi.encode(requestId, i))); diff --git a/src/test/mocks/LinkToken.sol b/test/mocks/LinkToken.sol similarity index 72% rename from src/test/mocks/LinkToken.sol rename to test/mocks/LinkToken.sol index 3fc0a08..f8d6af0 100644 --- a/src/test/mocks/LinkToken.sol +++ b/test/mocks/LinkToken.sol @@ -6,11 +6,7 @@ pragma solidity ^0.8.0; import "@solmate/tokens/ERC20.sol"; interface ERC677Receiver { - function onTokenTransfer( - address _sender, - uint256 _value, - bytes memory _data - ) external; + function onTokenTransfer(address _sender, uint256 _value, bytes memory _data) external; } contract LinkToken is ERC20 { @@ -21,12 +17,7 @@ contract LinkToken is ERC20 { _mint(msg.sender, INITIAL_SUPPLY); } - event Transfer( - address indexed from, - address indexed to, - uint256 value, - bytes data - ); + event Transfer(address indexed from, address indexed to, uint256 value, bytes data); /** * @dev transfer token to a contract address with additional data if the recipient is a contact. @@ -34,11 +25,7 @@ contract LinkToken is ERC20 { * @param _value The amount to be transferred. * @param _data The extra data to be passed to the receiving contract. */ - function transferAndCall( - address _to, - uint256 _value, - bytes memory _data - ) public virtual returns (bool success) { + function transferAndCall(address _to, uint256 _value, bytes memory _data) public virtual returns (bool success) { super.transfer(_to, _value); // emit Transfer(msg.sender, _to, _value, _data); emit Transfer(msg.sender, _to, _value, _data); @@ -50,11 +37,7 @@ contract LinkToken is ERC20 { // PRIVATE - function contractFallback( - address _to, - uint256 _value, - bytes memory _data - ) private { + function contractFallback(address _to, uint256 _value, bytes memory _data) private { ERC677Receiver receiver = ERC677Receiver(_to); receiver.onTokenTransfer(msg.sender, _value, _data); } diff --git a/src/test/mocks/MockOracle.sol b/test/mocks/MockOracle.sol similarity index 84% rename from src/test/mocks/MockOracle.sol rename to test/mocks/MockOracle.sol index 5b716ca..743d0ae 100644 --- a/src/test/mocks/MockOracle.sol +++ b/test/mocks/MockOracle.sol @@ -11,8 +11,7 @@ abstract contract LinkTokenReceiver { bytes4 private constant ORACLE_REQUEST_SELECTOR = 0x40429946; uint256 private constant SELECTOR_LENGTH = 4; uint256 private constant EXPECTED_REQUEST_WORDS = 2; - uint256 private constant MINIMUM_REQUEST_LENGTH = - SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); + uint256 private constant MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); /** * @notice Called when LINK is sent to the contract via `transferAndCall` @@ -22,11 +21,7 @@ abstract contract LinkTokenReceiver { * @param _amount Amount of LINK sent (specified in wei) * @param _data Payload of the transaction */ - function onTokenTransfer( - address _sender, - uint256 _amount, - bytes memory _data - ) + function onTokenTransfer(address _sender, uint256 _amount, bytes memory _data) public onlyLINK validRequestLength(_data) @@ -39,7 +34,7 @@ abstract contract LinkTokenReceiver { mstore(add(_data, 68), _amount) // ensure correct amount is passed } // solhint-disable-next-line avoid-low-level-calls - (bool success, ) = address(this).delegatecall(_data); // calls oracleRequest + (bool success,) = address(this).delegatecall(_data); // calls oracleRequest require(success, "Unable to create request"); } @@ -63,10 +58,7 @@ abstract contract LinkTokenReceiver { // solhint-disable-next-line avoid-low-level-calls funcSelector := mload(add(_data, 32)) } - require( - funcSelector == ORACLE_REQUEST_SELECTOR, - "Must use whitelisted functions" - ); + require(funcSelector == ORACLE_REQUEST_SELECTOR, "Must use whitelisted functions"); _; } @@ -75,10 +67,7 @@ abstract contract LinkTokenReceiver { * @param _data The request payload */ modifier validRequestLength(bytes memory _data) { - require( - _data.length >= MINIMUM_REQUEST_LENGTH, - "Invalid request length" - ); + require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); _; } } @@ -146,10 +135,7 @@ contract MockOracle is ChainlinkRequestInterface, LinkTokenReceiver { 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" - ); + require(commitments[requestId].callbackAddr == address(0), "Must use a unique ID"); // solhint-disable-next-line not-rely-on-time uint256 expiration = block.timestamp + EXPIRY_TIME; @@ -184,16 +170,11 @@ contract MockOracle is ChainlinkRequestInterface, LinkTokenReceiver { { Request memory req = commitments[_requestId]; delete commitments[_requestId]; - require( - gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, - "Must provide consumer enough gas" - ); + 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 + (bool success,) = req.callbackAddr.call(abi.encodeWithSelector(req.callbackFunctionId, _requestId, _data)); // solhint-disable-line avoid-low-level-calls return success; } @@ -206,16 +187,8 @@ contract MockOracle is ChainlinkRequestInterface, LinkTokenReceiver { * @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" - ); + 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 <= block.timestamp, "Request is not expired"); @@ -241,10 +214,7 @@ contract MockOracle is ChainlinkRequestInterface, LinkTokenReceiver { * @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" - ); + require(commitments[_requestId].callbackAddr != address(0), "Must have a valid requestId"); _; } diff --git a/src/test/mocks/MockV3Aggregator.sol b/test/mocks/MockV3Aggregator.sol similarity index 72% rename from src/test/mocks/MockV3Aggregator.sol rename to test/mocks/MockV3Aggregator.sol index 411c9b9..2969363 100644 --- a/src/test/mocks/MockV3Aggregator.sol +++ b/test/mocks/MockV3Aggregator.sol @@ -35,12 +35,7 @@ contract MockV3Aggregator { getStartedAt[latestRound] = block.timestamp; } - function updateRoundData( - uint80 _roundId, - int256 _answer, - uint256 _timestamp, - uint256 _startedAt - ) public { + function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public { latestRound = _roundId; latestAnswer = _answer; latestTimestamp = _timestamp; @@ -52,33 +47,15 @@ contract MockV3Aggregator { function getRoundData(uint80 _roundId) external view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ) + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { - return ( - _roundId, - getAnswer[_roundId], - getStartedAt[_roundId], - getTimestamp[_roundId], - _roundId - ); + return (_roundId, getAnswer[_roundId], getStartedAt[_roundId], getTimestamp[_roundId], _roundId); } function latestRoundData() external view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ) + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { return ( uint80(latestRound), diff --git a/src/test/mocks/MockVRFCoordinatorV2.sol b/test/mocks/MockVRFCoordinatorV2.sol similarity index 100% rename from src/test/mocks/MockVRFCoordinatorV2.sol rename to test/mocks/MockVRFCoordinatorV2.sol diff --git a/src/test/utils/Cheats.sol b/test/utils/Cheats.sol similarity index 68% rename from src/test/utils/Cheats.sol rename to test/utils/Cheats.sol index 1ffdaee..b81cee9 100644 --- a/src/test/utils/Cheats.sol +++ b/test/utils/Cheats.sol @@ -9,20 +9,11 @@ abstract contract Cheats { function roll(uint256 x) public virtual; // sets the slot loc of contract c to val - function store( - address c, - bytes32 loc, - bytes32 val - ) public virtual; + function store(address c, bytes32 loc, bytes32 val) public virtual; function ffi(string[] calldata) external virtual returns (bytes memory); - function expectEmit( - bool, - bool, - bool, - bool - ) external virtual; + function expectEmit(bool, bool, bool, bool) external virtual; function expectRevert(bytes calldata msg) external virtual; }