diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..032cbb5 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +GOERLI_RPC_URL=asdfasdf +PRIVATE_KEY=asdfasdf +ETHERSCAN_API_KEY=asdfasfs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a078a42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +cache/ +out/ +.gas-snapshot +.vscode/ +.env +.encryptedKey +broadcast/ + +node_modules +package-lock.json + +.DS_Store +src/.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..36dd8b1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,10 @@ +[submodule "lib/chainlink-brownie-contracts"] + path = lib/chainlink-brownie-contracts + url = https://github.com/smartcontractkit/chainlink-brownie-contracts +[submodule "lib/solmate"] + path = lib/solmate + url = https://github.com/transmissions11/solmate + branch = v7 +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..47cab42 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +build +coverage +out +lib +assets +node_modules +.next \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..87cf9d6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": false, + "singleQuote": false +} diff --git a/.solhint.json b/.solhint.json new file mode 100644 index 0000000..10945a7 --- /dev/null +++ b/.solhint.json @@ -0,0 +1,19 @@ +{ + "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/LICENSE b/LICENSE new file mode 100644 index 0000000..cbc35ef --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 2018 SmartContract ChainLink, Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eb6e920 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +-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-goerli :; @forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${GOERLI_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 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..42c50fe --- /dev/null +++ b/README.md @@ -0,0 +1,169 @@ +*Note: This repo has been recently updated for Goerli* + +# Foundry Starter Kit + +
+

+ +Chainlink Foundry logo + +

+
+ +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/smartcontractkit/foundry-starter-kit) + +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) + - [Quickstart](#quickstart) + - [Testing](#testing) +- [Deploying to a network](#deploying-to-a-network) + - [Setup](#setup) + - [Deploying](#deploying) + - [Working with a local network](#working-with-a-local-network) + - [Working with other chains](#working-with-other-chains) +- [Security](#security) +- [Contributing](#contributing) +- [Thank You!](#thank-you) + - [Resources](#resources) + - [TODO](#todo) + +# Getting Started + +## Requirements + +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) + +## 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 + +``` +make test +``` + +or + +``` +forge test +``` + +# Deploying to a network + +Deploying to a network uses the [foundry scripting system](https://book.getfoundry.sh/tutorials/solidity-scripting.html), where you write your deploy scripts in solidity! + +## Setup + +We'll demo using the Goerli testnet. (Go here for [testnet goerli ETH](https://faucets.chain.link/).) + +You'll need to add the following variables to a `.env` file: + +- `GOERLI_RPC_URL`: A URL to connect to the blockchain. You can get one for free from [Alchemy](https://www.alchemy.com/). +- `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 + +## Deploying + +``` +make deploy-goerli contract= +``` + +For example: + +``` +make deploy-goerli contract=PriceFeedConsumer +``` + +This will run the forge script, the script it's running is: + +``` +@forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${GOERLI_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: + +``` +@forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${GOERLI_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast +``` + +These pull from the files in the `script` folder. + +### Working with a local 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. + +To start a local network run: + +``` +make anvil +``` + +This will spin up a local blockchain with a determined private key, so you can use the same private key each time. + +Then, you can deploy to it with: + +``` +make deploy-anvil contract= +``` + +Similar to `deploy-goerli` + +### 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. + +``` +deploy- :; @forge script script/${contract}.s.sol:Deploy${contract} --rpc-url ${_RPC_URL} --private-key ${PRIVATE_KEY} --broadcast -vvvv + +``` + +# Security + +This framework comes with slither parameters, a popular security framework from [Trail of Bits](https://www.trailofbits.com/). To use slither, you'll first need to [install python](https://www.python.org/downloads/) and [install slither](https://github.com/crytic/slither#how-to-install). + +Then, you can run: + +``` +make slither +``` + +And get your slither output. + + + +# Contributing + +Contributions are always welcome! Open a PR or an issue! + +# Thank You! + +## Resources + +- [Chainlink Documentation](https://docs.chain.link/) +- [Foundry Documentation](https://book.getfoundry.sh/) + +### TODO + +[ ] Add bash scripts to interact with contracts using `cast` + +[ ] Make deploying contracts to `anvil` simpler diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..aea6e31 --- /dev/null +++ b/foundry.toml @@ -0,0 +1,11 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +gas_reports = ["*"] +optimizer = true +optimizer_runs = 20000 + +# Remappings in remappings.txt + +# See more config options https://github.com/gakonst/foundry/tree/master/config \ No newline at end of file diff --git a/img/chainlink-foundry.png b/img/chainlink-foundry.png new file mode 100755 index 0000000..cb324d8 Binary files /dev/null and b/img/chainlink-foundry.png differ diff --git a/lib/chainlink-brownie-contracts/.github/workflows/add-and-commit.yml b/lib/chainlink-brownie-contracts/.github/workflows/add-and-commit.yml new file mode 100644 index 0000000..7685005 --- /dev/null +++ b/lib/chainlink-brownie-contracts/.github/workflows/add-and-commit.yml @@ -0,0 +1,51 @@ +name: Update repo to match @chainlink/contracts + +on: + push: + schedule: + # Everyday at 3AM + - cron: '0 3 * * *' +jobs: + check-update-version-and-release: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [14.x] + steps: + - name: Checkout + uses: actions/checkout@v2 + # - name: Get Current Version + # run: current_version_var=$(cat version.txt) && echo "::set-output name=current_verision::${current_version_var}" + # id: get_current_version + # - name: print current version + # run: echo "Current @chainlink/contracts version is ${{steps.get_current_version.outputs.current_verision}}" + - name: Check npm version + run: yarn outdated + continue-on-error: true + id: check_outdated + - name: Get Updated Version + if: steps.check_outdated.outcome != 'success' + id: get_updated + run: | + yarn upgrade --latest + cp -r node_modules/@chainlink/contracts/* contracts/ + jq '.dependencies."@chainlink/contracts"' package.json | sed 's/"//g' | sed 's/\^//g' > version.txt + version=$(cat version.txt) + echo "::set-output name=version::${version}" + - name: add and commit + if: steps.check_outdated.outcome != 'success' + uses: stefanzweifel/git-auto-commit-action@v4 + with: + tagging_message: ${{steps.get_updated.outputs.version}} + commit_message: ${{steps.get_updated.outputs.version}} + - name: Create Release + if: steps.check_outdated.outcome != 'success' + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{steps.get_updated.outputs.version}} + release_name: ${{steps.get_updated.outputs.version}} + draft: false + prerelease: false diff --git a/lib/chainlink-brownie-contracts/.gitignore b/lib/chainlink-brownie-contracts/.gitignore new file mode 100644 index 0000000..84aea93 --- /dev/null +++ b/lib/chainlink-brownie-contracts/.gitignore @@ -0,0 +1,38 @@ +# dependencies +node_modules/ +tmp/ +.pnp +.pnp.js +tools/bin/abigen + +/chainlink +core/chainlink + +# GUI +operator_ui/dist +operator_ui/artifacts +*-packr.go + + +# SQLite +tools/clroot/db.sqlite3-shm +tools/clroot/db.sqlite3-wal + +# Tooling caches +*.tsbuildinfo +.eslintcache + +# Log files +*.log + +# misc +.DS_Store +.envrc +.env* + +# codeship +*.aes +dockercfg +env +credentials.env +gcr_creds.env diff --git a/lib/chainlink-brownie-contracts/README.md b/lib/chainlink-brownie-contracts/README.md new file mode 100644 index 0000000..b8c93d9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/README.md @@ -0,0 +1,10 @@ + +# Overview +This repository is a slimmed down version of Chainlink's official repo. It clones *only* the Chainlink `contracts` folder and the repo automatically updates every time there is a new NPM release. + +- NPM's latest release can be found here: https://www.npmjs.com/package/@chainlink/contracts +- Chainlink's official repo: https://github.com/smartcontractkit/chainlink + +## IMPORTANT +As of v1.2.0 and onward, all the releases of this package are going to match the [@chainlink/contracts NPM tags](https://www.npmjs.com/package/@chainlink/contracts). +So the versioning will look "backwards", but we are starting with v0.2.1 diff --git a/lib/chainlink-brownie-contracts/contracts/.gitignore b/lib/chainlink-brownie-contracts/contracts/.gitignore new file mode 100644 index 0000000..42d0872 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/.gitignore @@ -0,0 +1,4 @@ +artifacts +cache +node_modules +solc \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/.prettierignore b/lib/chainlink-brownie-contracts/contracts/.prettierignore new file mode 100644 index 0000000..46c4020 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/.prettierignore @@ -0,0 +1,4 @@ +# folders +artifacts/ +cache/** +node_modules/ diff --git a/lib/chainlink-brownie-contracts/contracts/.prettierrc b/lib/chainlink-brownie-contracts/contracts/.prettierrc new file mode 100644 index 0000000..528223d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/.prettierrc @@ -0,0 +1,17 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "endOfLine": "auto", + "printWidth": 120, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "all", + "overrides": [ + { + "files": "*.sol", + "options": { + "tabWidth": 4 + } + } + ] +} diff --git a/lib/chainlink-brownie-contracts/contracts/.solhint.json b/lib/chainlink-brownie-contracts/contracts/.solhint.json new file mode 100644 index 0000000..b9a5bba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/.solhint.json @@ -0,0 +1,14 @@ +{ + "extends": "solhint:recommended", + "plugins": [], + "rules": { + "quotes": ["error", "double"], + "avoid-suicide": "warn", + "avoid-sha3": "warn", + "avoid-low-level-calls": "warn", + "compiler-version": ["off", "^0.4.24"], + "avoid-throw": false, + "var-name-mixedcase": false, + "mark-callable-contracts": false + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/.solhintignore b/lib/chainlink-brownie-contracts/contracts/.solhintignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/.solhintignore @@ -0,0 +1 @@ +node_modules/ diff --git a/lib/chainlink-brownie-contracts/contracts/CHANGELOG.md b/lib/chainlink-brownie-contracts/contracts/CHANGELOG.md new file mode 100644 index 0000000..92af01b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/CHANGELOG.md @@ -0,0 +1,125 @@ +# @chainlink/contracts CHANGELOG.md + +## Unreleased + +... + +## 0.5.1 - .......... + +- Rename `KeeperBase` -> `AutomationBase` and add alias for backwards compatibility +- Rename `KeeperCompatible` -> `AutomationCompatible` and add alias for backwards compatibility +- Rename `KeeperCompatibleInterface` -> `AutomationCompatibleInterface` and add alias for backwards compatibility +- Rename `KeeperRegistryInterface1_2` -> `AutomationRegistryInterface1_2` and add alias for backwards compatibility + +## 0.5.0 - 2022-09-26 + +### Changed + +- Fix EIP-150 Bug in VRFV2Wrapper.sol (b9d8261eaa05838b9b609ea02005ecca3b6adca3) +- Added a new UpkeepFormat version `V2` in `UpkeepFormat` +- Renamed `KeeperRegistry` to `KeeperRegistry1_2` and `KeeperRegistryInterface` to `KeeperRegistryInterface1_2` +- Updated `UpkeepTranscoder` to only do a pass-through for upkeep bytes + +## 0.4.2 - 2022-07-20 + +### Changed + +- Downgrade 0.8.13 contracts to 0.8.6 due to [this solc bug](https://medium.com/certora/overly-optimistic-optimizer-certora-bug-disclosure-2101e3f7994d). +- Reintroduce v0.6 `EACAggregatorProxy` after removing it in [this commit](https://github.com/smartcontractkit/chainlink/commit/558f42f5122779cb2e05dc8c2b84d1ae78cc0d71) +- Ignore status update in `ArbitrumSequencerUptimeFeed` if incoming update has stale timestamp +- Revert to using current Arbitrum seq status flag in `ArbitrumSequencerUptimeFeed` +- Moved `VRFV2Wrapper`, `VRFV2WrapperConsumerBase` and `interfaces/VRFV2WrapperInterface` out of `dev` folder. + +## 0.4.1 - 2022-05-09 + +### Changed + +- VRFv2 contract pragma versions changed from `^0.8.0` to `^0.8.4`. + +## 0.4.0 - 2022-02-07 + +### Added + +- `ArbitrumSequencerUptimeFeedInterface` and `ArbitrumSequencerUptimeFeed` added in v0.8. + +### Changed + +- Changed `ArbitrumValidator#validate` target to `ArbitrumSequencerUptimeFeed` instead of + Flags contract. +- Moved `VRFConsumerBaseV2` out of dev + +## 0.3.1 - 2022-01-05 + +### Changed: + +- Fixed install issue with npm. + +## 0.3.0 - 2021-12-09 + +### Added + +- Prettier Solidity formatting applied to v0.7 and above. +- ERC677ReceiverInterface added in v0.8. +- `KeeperBase.sol` and `KeeperCompatible.sol` in Solidity v0.6 and v0.8 + +### Changed: + +- Operator Contract and Chainlink Client are officially supported. This enables + multiword requests/response are available through the ChainlinkClient by using + the newly enabled `buildOperatorRequest` along with `sendOperatorRequest` or + `sendOperatorRequestTo`. +- `ChainlinkClient` functions `requestOracleData` and `requestOracleDataFrom` have been changed to `sendChainlinkRequest` and + `sendChainlinkRequestTo` respectively. +- Updated function comments in `v0.6/interfaces/KeeperCompatibleInterface.sol` and `v0.8/interfaces/KeeperCompatibleInterface.sol` to match the latest in v0.7. +- Add `DelegateForwarderInterface` interface and `CrossDomainDelegateForwarder` base contract which implements a new `forwardDelegate()` function to forward delegatecalls from L1 to L2. + +## 0.2.2 - 2021-09-21 + +### Added: + +- v0.8 Access Controlled contracts (`SimpleWriteAccessController` and `SimpleReadAccessController`). +- v0.8 Flags contracts (`Flags`). +- v0.8 Contracts for the V2 VRF. `VRFCoordinatorV2.sol`, `VRF.sol`, + `VRFConsumerBaseV2.sol`, `VRFCoordinatorV2Interface.sol`. Along + with related test contract `VRFConsumerV2.sol` and example contracts + `VRFSingleConsumerExample.sol` and `VRFConsumerExternalSubOwnerExampl.sol`. +- v0.6 `MockV3Aggregator` in src/v0.6/tests/. +- v0.7 Added keeper-related smart contracts from the keeper repo. Added tests for `KeeperRegistry` and `UpkeepRegistrationRequests` in `test/v0.7/`. + +### Changed: + +- Move `Operator` and associated contracts (`AuthorizedForwarder`, `AuthorizedReceiver`, `LinkTokenReceiver`, `OperatorFactory`) from `./src/v0.7/dev/` to `./src/v0.7/`. +- Updated `Denominations` in `./src/` to include additional fiat currencies. +- Updated `./src/v0.8/vender/BufferChainlink.sol` with latest unchecked math version. + +## 0.2.1 - 2021-07-13 + +### Changed: + +- Bump hardhat from 2.3.3 to 2.4.1 +- Move Solidity version 0.8.x contracts `ChainlinkClient.sol`, `Chainlink.sol`, `VRFConsumerBase.sol` and `VRFRequestIDBase.sol` from `./src/v0.8/dev/` to `./src/v0.8/`. +- Updated `FeedRegistryInterface` to use `base` and `quote` parameter names. +- Move `Denominations` from `./src//dev/` to `./src/` + +## 0.2.0 - 2021-07-01 + +### Added: + +- `@chainlink/contracts` package changelog. +- `KeeperCompatibleInterface` contracts. +- Feeds Registry contracts: `FeedRegistryInterface` and `Denominations`. +- v0.8 Consumable contracts (`ChainlinkClient`, `VRFConsumerBase` and aggregator interfaces). +- Multi-word response handling in v0.7 and v0.8 `ChainlinkClient` contracts. + +### Changed: + +- Added missing licensees to `KeeperComptibleInterface`'s +- Upgrade solidity v8 compiler version from 0.8.4 to 0.8.6 +- Tests converted to Hardhat. +- Ethers upgraded from v4 to v5. +- Contract artifacts in `abi/` are now raw abi .json files, and do not include bytecode or other supplimentary data. + +### Removed: + +- Removed dependencies: `@chainlink/belt`, `@chainlink/test-helpers` and `@truffle`. +- Ethers and Truffle contract artifacts are no longer published. diff --git a/lib/chainlink-brownie-contracts/contracts/README.md b/lib/chainlink-brownie-contracts/contracts/README.md new file mode 100644 index 0000000..a10a9bb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/README.md @@ -0,0 +1,62 @@ +# Chainlink Smart Contracts + +## Installation + +```sh +# via Yarn +$ yarn add @chainlink/contracts +# via npm +$ npm install @chainlink/contracts --save +``` + +### Directory Structure + +```sh +@chainlink/contracts +├── src # Solidity contracts +│ ├── v0.4 +│ ├── v0.5 +│ ├── v0.6 +│ ├── v0.7 +│ └── v0.8 +└── abi # ABI json output + ├── v0.4 + ├── v0.5 + ├── v0.6 + ├── v0.7 + └── v0.8 +``` + +### Usage + +The solidity smart contracts themselves can be imported via the `src` directory of `@chainlink/contracts`: + +```solidity +import '@chainlink/contracts/src/v0.8/KeeperCompatibleInterface.sol'; + +``` + +## Local Development + +Note: Contracts in `dev/` directories are under active development and are likely unaudited. Please refrain from using these in production applications. + +```bash +# Clone Chainlink repository +$ git clone https://github.com/smartcontractkit/chainlink.git +# Continuing via Yarn +$ cd contracts/ +$ yarn +$ yarn test +``` + +## Contributing + +Contributions are welcome! Please refer to +[Chainlink's contributing guidelines](../docs/CONTRIBUTING.md) for detailed +contribution information. + +Thank you! + +## License + +[MIT](https://choosealicense.com/licenses/mit/) diff --git a/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/L1/messaging/IL1CrossDomainMessenger.sol/IL1CrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/L1/messaging/IL1CrossDomainMessenger.sol/IL1CrossDomainMessenger.json new file mode 100644 index 0000000..1a4c380 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/L1/messaging/IL1CrossDomainMessenger.sol/IL1CrossDomainMessenger.json @@ -0,0 +1,238 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "messageNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_messageNonce", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "batchRoot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "batchSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "prevTotalElements", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "extraData", + "type": "bytes" + } + ], + "internalType": "struct Lib_OVMCodec.ChainBatchHeader", + "name": "stateRootBatchHeader", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "siblings", + "type": "bytes32[]" + } + ], + "internalType": "struct Lib_OVMCodec.ChainInclusionProof", + "name": "stateRootProof", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "stateTrieWitness", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "storageTrieWitness", + "type": "bytes" + } + ], + "internalType": "struct IL1CrossDomainMessenger.L2MessageInclusionProof", + "name": "_proof", + "type": "tuple" + } + ], + "name": "relayMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_queueIndex", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "_oldGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_newGasLimit", + "type": "uint32" + } + ], + "name": "replayMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/L2/messaging/IL2CrossDomainMessenger.sol/IL2CrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/L2/messaging/IL2CrossDomainMessenger.sol/IL2CrossDomainMessenger.json new file mode 100644 index 0000000..79b6362 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/L2/messaging/IL2CrossDomainMessenger.sol/IL2CrossDomainMessenger.json @@ -0,0 +1,129 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "messageNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_messageNonce", + "type": "uint256" + } + ], + "name": "relayMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol/ICrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol/ICrossDomainMessenger.json new file mode 100644 index 0000000..4f70215 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol/ICrossDomainMessenger.json @@ -0,0 +1,101 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "messageNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/access/Ownable.sol/Ownable.json b/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/access/Ownable.sol/Ownable.json new file mode 100644 index 0000000..310a296 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/access/Ownable.sol/Ownable.json @@ -0,0 +1,54 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/proxy/Proxy.sol/Proxy.json b/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/proxy/Proxy.sol/Proxy.json new file mode 100644 index 0000000..c1dd512 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/proxy/Proxy.sol/Proxy.json @@ -0,0 +1,10 @@ +[ + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/security/Pausable.sol/Pausable.json b/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/security/Pausable.sol/Pausable.json new file mode 100644 index 0000000..9852560 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/@openzeppelin/contracts/security/Pausable.sol/Pausable.json @@ -0,0 +1,41 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Aggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Aggregator.json new file mode 100644 index 0000000..cdf37ae --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Aggregator.json @@ -0,0 +1,482 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "authorizedRequesters", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "jobIds", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minimumResponses", + "outputs": [ + { + "name": "", + "type": "uint128" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "oracles", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_recipient", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferLINK", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_clRequestId", + "type": "bytes32" + }, + { + "name": "_response", + "type": "int256" + } + ], + "name": "chainlinkCallback", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_paymentAmount", + "type": "uint128" + }, + { + "name": "_minimumResponses", + "type": "uint128" + }, + { + "name": "_oracles", + "type": "address[]" + }, + { + "name": "_jobIds", + "type": "bytes32[]" + } + ], + "name": "updateRequestDetails", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "destroy", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "paymentAmount", + "outputs": [ + { + "name": "", + "type": "uint128" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "requestRateUpdate", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requester", + "type": "address" + }, + { + "name": "_allowed", + "type": "bool" + } + ], + "name": "setAuthorization", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_paymentAmount", + "type": "uint128" + }, + { + "name": "_minimumResponses", + "type": "uint128" + }, + { + "name": "_oracles", + "type": "address[]" + }, + { + "name": "_jobIds", + "type": "bytes32[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "response", + "type": "int256" + }, + { + "indexed": true, + "name": "answerId", + "type": "uint256" + }, + { + "indexed": true, + "name": "sender", + "type": "address" + } + ], + "name": "ResponseReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + } + ], + "name": "OwnershipRenounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "name": "timestamp", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/AggregatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/AggregatorInterface.json new file mode 100644 index 0000000..e048175 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/AggregatorInterface.json @@ -0,0 +1,129 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/AggregatorV3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/AggregatorV3Interface.json new file mode 100644 index 0000000..9fe8722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/AggregatorV3Interface.json @@ -0,0 +1,113 @@ +[ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/BasicConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/BasicConsumer.json new file mode 100644 index 0000000..02ac2a0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/BasicConsumer.json @@ -0,0 +1,155 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_specId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/BasicToken.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/BasicToken.json new file mode 100644 index 0000000..fd9f922 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/BasicToken.json @@ -0,0 +1,80 @@ +[ + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ChainlinkClient.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ChainlinkClient.json new file mode 100644 index 0000000..15f4e3c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ChainlinkClient.json @@ -0,0 +1,38 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ChainlinkRequestInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ChainlinkRequestInterface.json new file mode 100644 index 0000000..f2c0bde --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ChainlinkRequestInterface.json @@ -0,0 +1,70 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "sender", + "type": "address" + }, + { + "name": "payment", + "type": "uint256" + }, + { + "name": "id", + "type": "bytes32" + }, + { + "name": "callbackAddress", + "type": "address" + }, + { + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "version", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "requestId", + "type": "bytes32" + }, + { + "name": "payment", + "type": "uint256" + }, + { + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Chainlinked.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Chainlinked.json new file mode 100644 index 0000000..15f4e3c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Chainlinked.json @@ -0,0 +1,38 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ConcreteChainlink.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ConcreteChainlink.json new file mode 100644 index 0000000..ef325a3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ConcreteChainlink.json @@ -0,0 +1,127 @@ +[ + { + "constant": false, + "inputs": [], + "name": "closeEvent", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "addUint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "bytes" + } + ], + "name": "addBytes", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_values", + "type": "bytes32[]" + } + ], + "name": "addStringArray", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "int256" + } + ], + "name": "addInt", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "data", + "type": "bytes" + } + ], + "name": "setBuffer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "string" + } + ], + "name": "add", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "payload", + "type": "bytes" + } + ], + "name": "RequestData", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ConcreteChainlinked.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ConcreteChainlinked.json new file mode 100644 index 0000000..2349963 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ConcreteChainlinked.json @@ -0,0 +1,292 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "publicLINK", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "publicCancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "publicAddExternalRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "fulfillRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_address", + "type": "address" + }, + { + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "publicChainlinkToken", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_address", + "type": "address" + }, + { + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestRunTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "publicFulfillChainlinkRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_address", + "type": "address" + }, + { + "name": "_fulfillmentSignature", + "type": "bytes" + } + ], + "name": "publicNewRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "publicOracleAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "name": "callbackAddress", + "type": "address" + }, + { + "indexed": false, + "name": "callbackfunctionSelector", + "type": "bytes4" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "Request", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "LinkAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Consumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Consumer.json new file mode 100644 index 0000000..0779f8a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Consumer.json @@ -0,0 +1,136 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENS.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENS.json new file mode 100644 index 0000000..d6d66ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENS.json @@ -0,0 +1,208 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "label", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "name": "", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSInterface.json new file mode 100644 index 0000000..d6d66ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSInterface.json @@ -0,0 +1,208 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "label", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "name": "", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSRegistry.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSRegistry.json new file mode 100644 index 0000000..d1669b4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSRegistry.json @@ -0,0 +1,214 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "label", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "name": "", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSResolver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSResolver.json new file mode 100644 index 0000000..84ca30b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ENSResolver.json @@ -0,0 +1,21 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC20.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC20.json new file mode 100644 index 0000000..6042f39 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC20.json @@ -0,0 +1,175 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC20Basic.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC20Basic.json new file mode 100644 index 0000000..81a4a73 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC20Basic.json @@ -0,0 +1,80 @@ +[ + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677.json new file mode 100644 index 0000000..a6dd36a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677.json @@ -0,0 +1,229 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677Receiver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677Receiver.json new file mode 100644 index 0000000..0ef3b52 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677Receiver.json @@ -0,0 +1,24 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677Token.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677Token.json new file mode 100644 index 0000000..a45fd2c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/ERC677Token.json @@ -0,0 +1,229 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/EmptyOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/EmptyOracle.json new file mode 100644 index 0000000..51adb0c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/EmptyOracle.json @@ -0,0 +1,200 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "bytes4" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "bytes4" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes4" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/FlagsInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/FlagsInterface.json new file mode 100644 index 0000000..0bba7df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/FlagsInterface.json @@ -0,0 +1,92 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/GetterSetter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/GetterSetter.json new file mode 100644 index 0000000..87bb976 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/GetterSetter.json @@ -0,0 +1,227 @@ +[ + { + "constant": true, + "inputs": [], + "name": "requestId", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBytes", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBytes32", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "requestedUint256", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_value", + "type": "bytes" + } + ], + "name": "requestedBytes", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getUint256", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "bytes32" + } + ], + "name": "setBytes32", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "uint256" + } + ], + "name": "setUint256", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "bytes" + } + ], + "name": "setBytes", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_value", + "type": "bytes32" + } + ], + "name": "requestedBytes32", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "value", + "type": "bytes32" + } + ], + "name": "SetBytes32", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "value", + "type": "uint256" + } + ], + "name": "SetUint256", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "bytes" + } + ], + "name": "SetBytes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "b32", + "type": "bytes32" + }, + { + "indexed": false, + "name": "u256", + "type": "uint256" + }, + { + "indexed": false, + "name": "b322", + "type": "bytes32" + } + ], + "name": "Output", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/LinkToken.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/LinkToken.json new file mode 100644 index 0000000..8985179 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/LinkToken.json @@ -0,0 +1,323 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/LinkTokenInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/LinkTokenInterface.json new file mode 100644 index 0000000..55a531c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/LinkTokenInterface.json @@ -0,0 +1,241 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "tokenName", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "totalTokensIssued", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "decimalPlaces", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "addedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "tokenSymbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousChainlinked.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousChainlinked.json new file mode 100644 index 0000000..15f4e3c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousChainlinked.json @@ -0,0 +1,38 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousConsumer.json new file mode 100644 index 0000000..6c64390 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousConsumer.json @@ -0,0 +1,193 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "doesNothing", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "stealEthCall", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "remove", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "stealEthSend", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "cancelRequestOnFulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "stealEthTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "requestData", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "assertFail", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousRequester.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousRequester.json new file mode 100644 index 0000000..f7916b9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/MaliciousRequester.json @@ -0,0 +1,163 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "maliciousRequestCancel", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "maliciousWithdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "doesNothing", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_target", + "type": "address" + } + ], + "name": "maliciousTargetConsumer", + "outputs": [ + { + "name": "requestId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_target", + "type": "address" + }, + { + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "request", + "outputs": [ + { + "name": "requestId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + } + ], + "name": "maliciousPrice", + "outputs": [ + { + "name": "requestId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Migrations.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Migrations.json new file mode 100644 index 0000000..f4a7ee4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Migrations.json @@ -0,0 +1,64 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "new_address", + "type": "address" + } + ], + "name": "upgrade", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "last_completed_migration", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "completed", + "type": "uint256" + } + ], + "name": "setCompleted", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Oracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Oracle.json new file mode 100644 index 0000000..38d925c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Oracle.json @@ -0,0 +1,355 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_specId", + "type": "bytes32" + }, + { + "name": "_callbackAddress", + "type": "address" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_nonce", + "type": "uint256" + }, + { + "name": "_dataVersion", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackAddress", + "type": "address" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "EXPIRY_TIME", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunc", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_node", + "type": "address" + }, + { + "name": "_allowed", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_node", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_recipient", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "specId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "name": "callbackAddr", + "type": "address" + }, + { + "indexed": false, + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "indexed": false, + "name": "cancelExpiration", + "type": "uint256" + }, + { + "indexed": false, + "name": "dataVersion", + "type": "uint256" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "OracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + } + ], + "name": "CancelOracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + } + ], + "name": "OwnershipRenounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/OracleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/OracleInterface.json new file mode 100644 index 0000000..8721835 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/OracleInterface.json @@ -0,0 +1,110 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "requestId", + "type": "bytes32" + }, + { + "name": "payment", + "type": "uint256" + }, + { + "name": "callbackAddress", + "type": "address" + }, + { + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "name": "expiration", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "address" + }, + { + "name": "allowed", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "recipient", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Ownable.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Ownable.json new file mode 100644 index 0000000..e495de5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Ownable.json @@ -0,0 +1,74 @@ +[ + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + } + ], + "name": "OwnershipRenounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Pointer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Pointer.json new file mode 100644 index 0000000..5bdd74f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/Pointer.json @@ -0,0 +1,27 @@ +[ + { + "constant": true, + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "_addr", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/PointerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/PointerInterface.json new file mode 100644 index 0000000..d00cf1f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/PointerInterface.json @@ -0,0 +1,16 @@ +[ + { + "constant": true, + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/PublicResolver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/PublicResolver.json new file mode 100644 index 0000000..5748e76 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/PublicResolver.json @@ -0,0 +1,448 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "key", + "type": "string" + }, + { + "name": "value", + "type": "string" + } + ], + "name": "setText", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "contentTypes", + "type": "uint256" + } + ], + "name": "ABI", + "outputs": [ + { + "name": "contentType", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "x", + "type": "bytes32" + }, + { + "name": "y", + "type": "bytes32" + } + ], + "name": "setPubkey", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "content", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "key", + "type": "string" + } + ], + "name": "text", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "contentType", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "setABI", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "name", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "hash", + "type": "bytes" + } + ], + "name": "setMultihash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "hash", + "type": "bytes32" + } + ], + "name": "setContent", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "pubkey", + "outputs": [ + { + "name": "x", + "type": "bytes32" + }, + { + "name": "y", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "addr", + "type": "address" + } + ], + "name": "setAddr", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "multihash", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "ensAddr", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "a", + "type": "address" + } + ], + "name": "AddrChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "hash", + "type": "bytes32" + } + ], + "name": "ContentChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "name", + "type": "string" + } + ], + "name": "NameChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "contentType", + "type": "uint256" + } + ], + "name": "ABIChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "x", + "type": "bytes32" + }, + { + "indexed": false, + "name": "y", + "type": "bytes32" + } + ], + "name": "PubkeyChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "indexedKey", + "type": "string" + }, + { + "indexed": false, + "name": "key", + "type": "string" + } + ], + "name": "TextChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "hash", + "type": "bytes" + } + ], + "name": "MultihashChanged", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/StandardToken.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/StandardToken.json new file mode 100644 index 0000000..a988108 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/StandardToken.json @@ -0,0 +1,221 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_addedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.4/UpdatableConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/UpdatableConsumer.json new file mode 100644 index 0000000..63551bb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.4/UpdatableConsumer.json @@ -0,0 +1,192 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOracle", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "updateOracle", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_specId", + "type": "bytes32" + }, + { + "name": "_ens", + "type": "address" + }, + { + "name": "_node", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorInterface.json new file mode 100644 index 0000000..e048175 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorInterface.json @@ -0,0 +1,129 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorV2V3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorV2V3Interface.json new file mode 100644 index 0000000..b796423 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorV2V3Interface.json @@ -0,0 +1,240 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorV3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorV3Interface.json new file mode 100644 index 0000000..9fe8722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/AggregatorV3Interface.json @@ -0,0 +1,113 @@ +[ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/BasicConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/BasicConsumer.json new file mode 100644 index 0000000..d8d0be7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/BasicConsumer.json @@ -0,0 +1,203 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callback", + "type": "address" + } + ], + "name": "requestEthereumPriceByCallback", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + }, + { + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_specId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkClient.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkClient.json new file mode 100644 index 0000000..15f4e3c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkClient.json @@ -0,0 +1,38 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkRequestInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkRequestInterface.json new file mode 100644 index 0000000..84354ec --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkRequestInterface.json @@ -0,0 +1,70 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "sender", + "type": "address" + }, + { + "name": "requestPrice", + "type": "uint256" + }, + { + "name": "serviceAgreementID", + "type": "bytes32" + }, + { + "name": "callbackAddress", + "type": "address" + }, + { + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "dataVersion", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "requestId", + "type": "bytes32" + }, + { + "name": "payment", + "type": "uint256" + }, + { + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkTestHelper.json new file mode 100644 index 0000000..ef325a3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ChainlinkTestHelper.json @@ -0,0 +1,127 @@ +[ + { + "constant": false, + "inputs": [], + "name": "closeEvent", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "addUint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "bytes" + } + ], + "name": "addBytes", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_values", + "type": "bytes32[]" + } + ], + "name": "addStringArray", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "int256" + } + ], + "name": "addInt", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "data", + "type": "bytes" + } + ], + "name": "setBuffer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_key", + "type": "string" + }, + { + "name": "_value", + "type": "string" + } + ], + "name": "add", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "payload", + "type": "bytes" + } + ], + "name": "RequestData", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Consumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Consumer.json new file mode 100644 index 0000000..af9d121 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Consumer.json @@ -0,0 +1,184 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callback", + "type": "address" + } + ], + "name": "requestEthereumPriceByCallback", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + }, + { + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Coordinator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Coordinator.json new file mode 100644 index 0000000..eb449a3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Coordinator.json @@ -0,0 +1,380 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "withdrawableTokens", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_sAId", + "type": "bytes32" + }, + { + "name": "_callbackAddress", + "type": "address" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_nonce", + "type": "uint256" + }, + { + "name": "_dataVersion", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "EXPIRY_TIME", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_agreementData", + "type": "bytes" + } + ], + "name": "getId", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "bytes4" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "serviceAgreements", + "outputs": [ + { + "name": "payment", + "type": "uint256" + }, + { + "name": "expiration", + "type": "uint256" + }, + { + "name": "endAt", + "type": "uint256" + }, + { + "name": "requestDigest", + "type": "bytes32" + }, + { + "name": "aggregator", + "type": "address" + }, + { + "name": "aggInitiateJobSelector", + "type": "bytes4" + }, + { + "name": "aggFulfillSelector", + "type": "bytes4" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "depositFunds", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_serviceAgreementData", + "type": "bytes" + }, + { + "name": "_oracleSignaturesData", + "type": "bytes" + } + ], + "name": "initiateServiceAgreement", + "outputs": [ + { + "name": "serviceAgreementID", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_recipient", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "sAId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "name": "callbackAddr", + "type": "address" + }, + { + "indexed": false, + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "indexed": false, + "name": "cancelExpiration", + "type": "uint256" + }, + { + "indexed": false, + "name": "dataVersion", + "type": "uint256" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "OracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "said", + "type": "bytes32" + }, + { + "indexed": true, + "name": "requestDigest", + "type": "bytes32" + } + ], + "name": "NewServiceAgreement", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "internalId", + "type": "bytes32" + } + ], + "name": "CancelOracleRequest", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/CoordinatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/CoordinatorInterface.json new file mode 100644 index 0000000..42792f4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/CoordinatorInterface.json @@ -0,0 +1,48 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_aggregatorArgs", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_serviceAgreementData", + "type": "bytes" + }, + { + "name": "_oracleSignaturesData", + "type": "bytes" + } + ], + "name": "initiateServiceAgreement", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ENSInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ENSInterface.json new file mode 100644 index 0000000..76cf1c4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ENSInterface.json @@ -0,0 +1,208 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "label", + "type": "bytes32" + }, + { + "name": "_owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "_ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "name": "", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "_resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "bytes32" + }, + { + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ENSResolver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ENSResolver.json new file mode 100644 index 0000000..84ca30b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ENSResolver.json @@ -0,0 +1,21 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/EmptyAggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/EmptyAggregator.json new file mode 100644 index 0000000..6a8418f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/EmptyAggregator.json @@ -0,0 +1,116 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_saId", + "type": "bytes32" + }, + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_fulfillment", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [ + { + "name": "success", + "type": "bool" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "response", + "type": "bytes" + }, + { + "name": "paymentAmounts", + "type": "int256[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_saId", + "type": "bytes32" + }, + { + "name": "_serviceAgreementData", + "type": "bytes" + } + ], + "name": "initiateJob", + "outputs": [ + { + "name": "success", + "type": "bool" + }, + { + "name": "_", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "said", + "type": "bytes32" + } + ], + "name": "InitiatedJob", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "oracle", + "type": "address" + }, + { + "indexed": false, + "name": "success", + "type": "bool" + }, + { + "indexed": false, + "name": "complete", + "type": "bool" + }, + { + "indexed": false, + "name": "fulfillment", + "type": "bytes" + } + ], + "name": "Fulfilled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/FlagsInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/FlagsInterface.json new file mode 100644 index 0000000..0bba7df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/FlagsInterface.json @@ -0,0 +1,92 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/GetterSetter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/GetterSetter.json new file mode 100644 index 0000000..87bb976 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/GetterSetter.json @@ -0,0 +1,227 @@ +[ + { + "constant": true, + "inputs": [], + "name": "requestId", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBytes", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBytes32", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "requestedUint256", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_value", + "type": "bytes" + } + ], + "name": "requestedBytes", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getUint256", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "bytes32" + } + ], + "name": "setBytes32", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "uint256" + } + ], + "name": "setUint256", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "bytes" + } + ], + "name": "setBytes", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_value", + "type": "bytes32" + } + ], + "name": "requestedBytes32", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "value", + "type": "bytes32" + } + ], + "name": "SetBytes32", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "value", + "type": "uint256" + } + ], + "name": "SetUint256", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "bytes" + } + ], + "name": "SetBytes", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "b32", + "type": "bytes32" + }, + { + "indexed": false, + "name": "u256", + "type": "uint256" + }, + { + "indexed": false, + "name": "b322", + "type": "bytes32" + } + ], + "name": "Output", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/LinkTokenInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/LinkTokenInterface.json new file mode 100644 index 0000000..55a531c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/LinkTokenInterface.json @@ -0,0 +1,241 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "tokenName", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "totalTokensIssued", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "decimalPlaces", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "addedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "tokenSymbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/LinkTokenReceiver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/LinkTokenReceiver.json new file mode 100644 index 0000000..c5bd883 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/LinkTokenReceiver.json @@ -0,0 +1,38 @@ +[ + { + "constant": true, + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousChainlinkClient.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousChainlinkClient.json new file mode 100644 index 0000000..15f4e3c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousChainlinkClient.json @@ -0,0 +1,38 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousConsumer.json new file mode 100644 index 0000000..6c64390 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousConsumer.json @@ -0,0 +1,193 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "doesNothing", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "stealEthCall", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "remove", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "stealEthSend", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "cancelRequestOnFulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "stealEthTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "requestData", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "assertFail", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousRequester.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousRequester.json new file mode 100644 index 0000000..f7916b9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MaliciousRequester.json @@ -0,0 +1,163 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "maliciousRequestCancel", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "maliciousWithdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes32" + } + ], + "name": "doesNothing", + "outputs": [], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_target", + "type": "address" + } + ], + "name": "maliciousTargetConsumer", + "outputs": [ + { + "name": "requestId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + }, + { + "name": "_target", + "type": "address" + }, + { + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "request", + "outputs": [ + { + "name": "requestId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_id", + "type": "bytes32" + } + ], + "name": "maliciousPrice", + "outputs": [ + { + "name": "requestId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_oracle", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MeanAggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MeanAggregator.json new file mode 100644 index 0000000..7789998 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MeanAggregator.json @@ -0,0 +1,72 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_sAId", + "type": "bytes32" + }, + { + "name": "_oracle", + "type": "address" + }, + { + "name": "_value", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [ + { + "name": "success", + "type": "bool" + }, + { + "name": "complete", + "type": "bool" + }, + { + "name": "response", + "type": "bytes" + }, + { + "name": "paymentAmounts", + "type": "int256[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sAId", + "type": "bytes32" + }, + { + "name": "_serviceAgreementData", + "type": "bytes" + } + ], + "name": "initiateJob", + "outputs": [ + { + "name": "success", + "type": "bool" + }, + { + "name": "message", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MedianTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MedianTestHelper.json new file mode 100644 index 0000000..b657b23 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/MedianTestHelper.json @@ -0,0 +1,21 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "_list", + "type": "int256[]" + } + ], + "name": "publicGet", + "outputs": [ + { + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Migrations.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Migrations.json new file mode 100644 index 0000000..f4a7ee4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Migrations.json @@ -0,0 +1,64 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "new_address", + "type": "address" + } + ], + "name": "upgrade", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "last_completed_migration", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "completed", + "type": "uint256" + } + ], + "name": "setCompleted", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Oracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Oracle.json new file mode 100644 index 0000000..5c54e90 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Oracle.json @@ -0,0 +1,362 @@ +[ + { + "constant": true, + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_specId", + "type": "bytes32" + }, + { + "name": "_callbackAddress", + "type": "address" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_nonce", + "type": "uint256" + }, + { + "name": "_dataVersion", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackAddress", + "type": "address" + }, + { + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "EXPIRY_TIME", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_payment", + "type": "uint256" + }, + { + "name": "_callbackFunc", + "type": "bytes4" + }, + { + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_node", + "type": "address" + }, + { + "name": "_allowed", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_node", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_recipient", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "specId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "name": "callbackAddr", + "type": "address" + }, + { + "indexed": false, + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "indexed": false, + "name": "cancelExpiration", + "type": "uint256" + }, + { + "indexed": false, + "name": "dataVersion", + "type": "uint256" + }, + { + "indexed": false, + "name": "data", + "type": "bytes" + } + ], + "name": "OracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "requestId", + "type": "bytes32" + } + ], + "name": "CancelOracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/OracleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/OracleInterface.json new file mode 100644 index 0000000..8721835 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/OracleInterface.json @@ -0,0 +1,110 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "requestId", + "type": "bytes32" + }, + { + "name": "payment", + "type": "uint256" + }, + { + "name": "callbackAddress", + "type": "address" + }, + { + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "name": "expiration", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "node", + "type": "address" + }, + { + "name": "allowed", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "node", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "recipient", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Ownable.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Ownable.json new file mode 100644 index 0000000..25c3918 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/Ownable.json @@ -0,0 +1,67 @@ +[ + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/PointerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/PointerInterface.json new file mode 100644 index 0000000..d00cf1f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/PointerInterface.json @@ -0,0 +1,16 @@ +[ + { + "constant": true, + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/SchnorrSECP256K1.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/SchnorrSECP256K1.json new file mode 100644 index 0000000..225981b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/SchnorrSECP256K1.json @@ -0,0 +1,65 @@ +[ + { + "constant": true, + "inputs": [], + "name": "HALF_Q", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "Q", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "signingPubKeyX", + "type": "uint256" + }, + { + "name": "pubKeyYParity", + "type": "uint8" + }, + { + "name": "signature", + "type": "uint256" + }, + { + "name": "msgHash", + "type": "uint256" + }, + { + "name": "nonceTimesGeneratorAddress", + "type": "address" + } + ], + "name": "verifySignature", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ServiceAgreementConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ServiceAgreementConsumer.json new file mode 100644 index 0000000..3d35690 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/ServiceAgreementConsumer.json @@ -0,0 +1,103 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "_requestId", + "type": "bytes32" + }, + { + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_currency", + "type": "string" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "_link", + "type": "address" + }, + { + "name": "_coordinator", + "type": "address" + }, + { + "name": "_sAId", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.5/WithdrawalInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/WithdrawalInterface.json new file mode 100644 index 0000000..feade7f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.5/WithdrawalInterface.json @@ -0,0 +1,34 @@ +[ + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "recipient", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AccessControllerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AccessControllerInterface.json new file mode 100644 index 0000000..d018c95 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AccessControllerInterface.json @@ -0,0 +1,26 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorFacade.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorFacade.json new file mode 100644 index 0000000..91fa314 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorFacade.json @@ -0,0 +1,274 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "string", + "name": "_description", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "contract AggregatorInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorInterface.json new file mode 100644 index 0000000..7fb29aa --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorInterface.json @@ -0,0 +1,129 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorProxy.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorProxy.json new file mode 100644 index 0000000..78dae8d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorProxy.json @@ -0,0 +1,478 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "confirmAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "proposeAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorV2V3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorV2V3Interface.json new file mode 100644 index 0000000..4667a49 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorV2V3Interface.json @@ -0,0 +1,240 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorV3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorV3Interface.json new file mode 100644 index 0000000..9fe8722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorV3Interface.json @@ -0,0 +1,113 @@ +[ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorValidatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorValidatorInterface.json new file mode 100644 index 0000000..54a6a92 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorValidatorInterface.json @@ -0,0 +1,36 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "previousRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "currentRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorValidatorMock.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorValidatorMock.json new file mode 100644 index 0000000..6388317 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/AggregatorValidatorMock.json @@ -0,0 +1,119 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_previousRoundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "int256", + "name": "_previousAnswer", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_currentRoundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "int256", + "name": "_currentAnswer", + "type": "int256" + } + ], + "name": "Validated", + "type": "event" + }, + { + "inputs": [], + "name": "currentAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "currentRoundId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "previousAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "previousRoundId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_previousRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_currentRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BasicConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BasicConsumer.json new file mode 100644 index 0000000..dacf2bb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BasicConsumer.json @@ -0,0 +1,211 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_callback", + "type": "address" + } + ], + "name": "requestEthereumPriceByCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockHashStoreInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockHashStoreInterface.json new file mode 100644 index 0000000..7e9ac19 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockHashStoreInterface.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + } + ], + "name": "getBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockhashStore.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockhashStore.json new file mode 100644 index 0000000..2896962 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockhashStore.json @@ -0,0 +1,59 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "getBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "store", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "storeEarliest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "storeVerifyHeader", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockhashStoreTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockhashStoreTestHelper.json new file mode 100644 index 0000000..ca09e42 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/BlockhashStoreTestHelper.json @@ -0,0 +1,77 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "getBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "h", + "type": "bytes32" + } + ], + "name": "godmodeSetHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "store", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "storeEarliest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "storeVerifyHeader", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkClient.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkClient.json new file mode 100644 index 0000000..57999bd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkClient.json @@ -0,0 +1,41 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkClientTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkClientTestHelper.json new file mode 100644 index 0000000..d1a1bbf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkClientTestHelper.json @@ -0,0 +1,306 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "LinkAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "callbackfunctionSelector", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "Request", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "fulfillRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "publicAddExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "publicCancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "publicChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "publicFulfillChainlinkRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "publicLINK", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + } + ], + "name": "publicNewRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "publicOracleAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestRunTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkRequestInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkRequestInterface.json new file mode 100644 index 0000000..35ea584 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkRequestInterface.json @@ -0,0 +1,78 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestPrice", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "serviceAgreementID", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkTestHelper.json new file mode 100644 index 0000000..225b24d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ChainlinkTestHelper.json @@ -0,0 +1,125 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "payload", + "type": "bytes" + } + ], + "name": "RequestData", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "string", + "name": "_value", + "type": "string" + } + ], + "name": "add", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "bytes", + "name": "_value", + "type": "bytes" + } + ], + "name": "addBytes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "int256", + "name": "_value", + "type": "int256" + } + ], + "name": "addInt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "bytes32[]", + "name": "_values", + "type": "bytes32[]" + } + ], + "name": "addStringArray", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "addUint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "closeEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "setBuffer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/CheckedMathTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/CheckedMathTestHelper.json new file mode 100644 index 0000000..962078c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/CheckedMathTestHelper.json @@ -0,0 +1,118 @@ +[ + { + "inputs": [ + { + "internalType": "int256", + "name": "a", + "type": "int256" + }, + { + "internalType": "int256", + "name": "b", + "type": "int256" + } + ], + "name": "add", + "outputs": [ + { + "internalType": "int256", + "name": "result", + "type": "int256" + }, + { + "internalType": "bool", + "name": "ok", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "a", + "type": "int256" + }, + { + "internalType": "int256", + "name": "b", + "type": "int256" + } + ], + "name": "div", + "outputs": [ + { + "internalType": "int256", + "name": "result", + "type": "int256" + }, + { + "internalType": "bool", + "name": "ok", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "a", + "type": "int256" + }, + { + "internalType": "int256", + "name": "b", + "type": "int256" + } + ], + "name": "mul", + "outputs": [ + { + "internalType": "int256", + "name": "result", + "type": "int256" + }, + { + "internalType": "bool", + "name": "ok", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "a", + "type": "int256" + }, + { + "internalType": "int256", + "name": "b", + "type": "int256" + } + ], + "name": "sub", + "outputs": [ + { + "internalType": "int256", + "name": "result", + "type": "int256" + }, + { + "internalType": "bool", + "name": "ok", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ConcreteSignedSafeMath.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ConcreteSignedSafeMath.json new file mode 100644 index 0000000..a959fb9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ConcreteSignedSafeMath.json @@ -0,0 +1,50 @@ +[ + { + "inputs": [ + { + "internalType": "int256", + "name": "_a", + "type": "int256" + }, + { + "internalType": "int256", + "name": "_b", + "type": "int256" + } + ], + "name": "testAdd", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_a", + "type": "int256" + }, + { + "internalType": "int256", + "name": "_b", + "type": "int256" + } + ], + "name": "testAvg", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Consumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Consumer.json new file mode 100644 index 0000000..25e2c97 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Consumer.json @@ -0,0 +1,190 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_callback", + "type": "address" + } + ], + "name": "requestEthereumPriceByCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Denominations.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Denominations.json new file mode 100644 index 0000000..1d16b21 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Denominations.json @@ -0,0 +1,262 @@ +[ + { + "inputs": [], + "name": "ARS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "AUD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BRL", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BTC", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CAD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CHF", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CNY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EUR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GBP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "JPY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "KRW", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NGN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NZD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PHP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RUB", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SGD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "USD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ZAR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/DeviationFlaggingValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/DeviationFlaggingValidator.json new file mode 100644 index 0000000..0956ba8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/DeviationFlaggingValidator.json @@ -0,0 +1,260 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_flags", + "type": "address" + }, + { + "internalType": "uint24", + "name": "_flaggingThreshold", + "type": "uint24" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "previous", + "type": "uint24" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "current", + "type": "uint24" + } + ], + "name": "FlaggingThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "FlagsAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "THRESHOLD_MULTIPLIER", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "flaggingThreshold", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "contract FlagsInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + } + ], + "name": "isValid", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "_flaggingThreshold", + "type": "uint24" + } + ], + "name": "setFlaggingThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_flags", + "type": "address" + } + ], + "name": "setFlagsAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_previousRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/EACAggregatorProxy.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/EACAggregatorProxy.json new file mode 100644 index 0000000..bca8a84 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/EACAggregatorProxy.json @@ -0,0 +1,509 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "address", + "name": "_accessController", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessController", + "outputs": [ + { + "internalType": "contract AccessControllerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "confirmAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "proposeAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_accessController", + "type": "address" + } + ], + "name": "setController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ENSInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ENSInterface.json new file mode 100644 index 0000000..3ef0557 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ENSInterface.json @@ -0,0 +1,218 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "label", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "_ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ENSResolver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ENSResolver.json new file mode 100644 index 0000000..44ea5ee --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/ENSResolver.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/EmptyOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/EmptyOracle.json new file mode 100644 index 0000000..20e35a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/EmptyOracle.json @@ -0,0 +1,213 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FeedRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FeedRegistryInterface.json new file mode 100644 index 0000000..5c017df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FeedRegistryInterface.json @@ -0,0 +1,803 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "denomination", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "latestAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "previousAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "nextPhaseId", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeedConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "denomination", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "proposedAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "currentAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeedProposed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "confirmFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getCurrentPhaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "currentPhaseId", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getNextRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "nextRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhase", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + }, + { + "internalType": "uint80", + "name": "startingAggregatorRoundId", + "type": "uint80" + }, + { + "internalType": "uint80", + "name": "endingAggregatorRoundId", + "type": "uint80" + } + ], + "internalType": "struct FeedRegistryInterface.Phase", + "name": "phase", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhaseFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhaseRange", + "outputs": [ + { + "internalType": "uint80", + "name": "startingRoundId", + "type": "uint80" + }, + { + "internalType": "uint80", + "name": "endingRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getPreviousRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "previousRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getProposedFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "proposedAggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getRoundFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "isFeedEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "proposeFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Flags.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Flags.json new file mode 100644 index 0000000..a7cbc9c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Flags.json @@ -0,0 +1,347 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "racAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "FlagLowered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "FlagRaised", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "RaisingAccessControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "raisingAccessController", + "outputs": [ + { + "internalType": "contract AccessControllerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "racAddress", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FlagsInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FlagsInterface.json new file mode 100644 index 0000000..0bba7df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FlagsInterface.json @@ -0,0 +1,92 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FlagsTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FlagsTestHelper.json new file mode 100644 index 0000000..f42ff6d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FlagsTestHelper.json @@ -0,0 +1,64 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsContract", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "contract Flags", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FluxAggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FluxAggregator.json new file mode 100644 index 0000000..55758a4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FluxAggregator.json @@ -0,0 +1,1045 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "uint128", + "name": "_paymentAmount", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "_timeout", + "type": "uint32" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "int256", + "name": "_minSubmissionValue", + "type": "int256" + }, + { + "internalType": "int256", + "name": "_maxSubmissionValue", + "type": "int256" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "string", + "name": "_description", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "AvailableFundsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "OracleAdminUpdateRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "OracleAdminUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "indexed": true, + "internalType": "bool", + "name": "whitelisted", + "type": "bool" + } + ], + "name": "OraclePermissionsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "authorized", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "RequesterPermissionsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint128", + "name": "paymentAmount", + "type": "uint128" + }, + { + "indexed": true, + "internalType": "uint32", + "name": "minSubmissionCount", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "uint32", + "name": "maxSubmissionCount", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "restartDelay", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "timeout", + "type": "uint32" + } + ], + "name": "RoundDetailsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "submission", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint32", + "name": "round", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + } + ], + "name": "SubmissionReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "ValidatorUpdated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "name": "acceptAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "allocatedFunds", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "availableFunds", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_removed", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_added", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_addedAdmins", + "type": "address[]" + }, + { + "internalType": "uint32", + "name": "_minSubmissions", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_maxSubmissions", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_restartDelay", + "type": "uint32" + } + ], + "name": "changeOracles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "name": "getAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOracles", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "linkToken", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSubmissionCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSubmissionValue", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minSubmissionCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minSubmissionValue", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "oracleCount", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "uint32", + "name": "_queriedRoundId", + "type": "uint32" + } + ], + "name": "oracleRoundState", + "outputs": [ + { + "internalType": "bool", + "name": "_eligibleToSubmit", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "_roundId", + "type": "uint32" + }, + { + "internalType": "int256", + "name": "_latestSubmission", + "type": "int256" + }, + { + "internalType": "uint64", + "name": "_startedAt", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "_timeout", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "_availableFunds", + "type": "uint128" + }, + { + "internalType": "uint8", + "name": "_oracleCount", + "type": "uint8" + }, + { + "internalType": "uint128", + "name": "_paymentAmount", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paymentAmount", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "requestNewRound", + "outputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "restartDelay", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_requester", + "type": "address" + }, + { + "internalType": "bool", + "name": "_authorized", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "_delay", + "type": "uint32" + } + ], + "name": "setRequesterPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newValidator", + "type": "address" + } + ], + "name": "setValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_submission", + "type": "int256" + } + ], + "name": "submit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "timeout", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "address", + "name": "_newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "updateAvailableFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "_paymentAmount", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "_minSubmissions", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_maxSubmissions", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_restartDelay", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_timeout", + "type": "uint32" + } + ], + "name": "updateFutureRounds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "validator", + "outputs": [ + { + "internalType": "contract AggregatorValidatorInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "name": "withdrawablePayment", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FluxAggregatorTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FluxAggregatorTestHelper.json new file mode 100644 index 0000000..6a7594b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/FluxAggregatorTestHelper.json @@ -0,0 +1,178 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_roundID", + "type": "uint256" + } + ], + "name": "readGetAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "uint80", + "name": "_roundID", + "type": "uint80" + } + ], + "name": "readGetRoundData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_roundID", + "type": "uint256" + } + ], + "name": "readGetTimestamp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "readLatestAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "readLatestRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "readLatestRoundData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "readLatestTimestamp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "name": "readOracleRoundState", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_aggregator", + "type": "address" + } + ], + "name": "requestNewRound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "requestedRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/GasGuzzler.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/GasGuzzler.json new file mode 100644 index 0000000..ddd6adb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/GasGuzzler.json @@ -0,0 +1,6 @@ +[ + { + "stateMutability": "payable", + "type": "fallback" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/GasGuzzlingConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/GasGuzzlingConsumer.json new file mode 100644 index 0000000..95b8d5b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/GasGuzzlingConsumer.json @@ -0,0 +1,273 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_price", + "type": "bytes32" + } + ], + "name": "gassyFulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_price", + "type": "bytes" + } + ], + "name": "gassyMultiWordFulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "gassyMultiWordRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "gassyRequestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_callback", + "type": "address" + } + ], + "name": "requestEthereumPriceByCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatible.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatible.json new file mode 100644 index 0000000..e934ed8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatible.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatibleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatibleInterface.json new file mode 100644 index 0000000..e934ed8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatibleInterface.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatibleTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatibleTestHelper.json new file mode 100644 index 0000000..adb26a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/KeeperCompatibleTestHelper.json @@ -0,0 +1,46 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "testCannotExecute", + "outputs": [], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/LinkTokenInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/LinkTokenInterface.json new file mode 100644 index 0000000..dc470f1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/LinkTokenInterface.json @@ -0,0 +1,245 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "remaining", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "decimalPlaces", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "totalTokensIssued", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/LinkTokenReceiver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/LinkTokenReceiver.json new file mode 100644 index 0000000..3c28478 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/LinkTokenReceiver.json @@ -0,0 +1,38 @@ +[ + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MaliciousMultiWordConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MaliciousMultiWordConsumer.json new file mode 100644 index 0000000..3f3c063 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MaliciousMultiWordConsumer.json @@ -0,0 +1,194 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "assertFail", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "cancelRequestOnFulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "doesNothing", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "remove", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_callbackFunc", + "type": "bytes" + } + ], + "name": "requestData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "stealEthCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "stealEthSend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "stealEthTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MedianTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MedianTestHelper.json new file mode 100644 index 0000000..b694b15 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MedianTestHelper.json @@ -0,0 +1,55 @@ +[ + { + "inputs": [ + { + "internalType": "int256[]", + "name": "list", + "type": "int256[]" + } + ], + "name": "publicGet", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256[]", + "name": "list", + "type": "int256[]" + }, + { + "internalType": "uint256", + "name": "k1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "k2", + "type": "uint256" + } + ], + "name": "publicQuickselectTwo", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MockOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MockOracle.json new file mode 100644 index 0000000..71c855f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MockOracle.json @@ -0,0 +1,236 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "name": "CancelOracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "callbackAddr", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cancelExpiration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "OracleRequest", + "type": "event" + }, + { + "inputs": [], + "name": "EXPIRY_TIME", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MockV3Aggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MockV3Aggregator.json new file mode 100644 index 0000000..4c1909f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MockV3Aggregator.json @@ -0,0 +1,297 @@ +[ + { + "inputs": [ + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "int256", + "name": "_initialAnswer", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + } + ], + "name": "updateAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startedAt", + "type": "uint256" + } + ], + "name": "updateRoundData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MultiWordConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MultiWordConsumer.json new file mode 100644 index 0000000..ad697e8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/MultiWordConsumer.json @@ -0,0 +1,303 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes", + "name": "price", + "type": "bytes" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "first", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "second", + "type": "bytes32" + } + ], + "name": "RequestMultipleFulfilled", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "first", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_price", + "type": "bytes" + } + ], + "name": "fulfillBytes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_first", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_second", + "type": "bytes32" + } + ], + "name": "fulfillMultipleParameters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_callback", + "type": "address" + } + ], + "name": "requestEthereumPriceByCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestMultipleParameters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "second", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Oracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Oracle.json new file mode 100644 index 0000000..cbfe5c9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Oracle.json @@ -0,0 +1,382 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "name": "CancelOracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "callbackAddr", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cancelExpiration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "OracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "EXPIRY_TIME", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunc", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_node", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_node", + "type": "address" + }, + { + "internalType": "bool", + "name": "_allowed", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/OracleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/OracleInterface.json new file mode 100644 index 0000000..a110adb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/OracleInterface.json @@ -0,0 +1,114 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "node", + "type": "address" + } + ], + "name": "getAuthorizationStatus", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "node", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setFulfillmentPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Ownable.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Ownable.json new file mode 100644 index 0000000..4fd0cd7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Ownable.json @@ -0,0 +1,65 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Owned.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Owned.json new file mode 100644 index 0000000..fa455e7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Owned.json @@ -0,0 +1,78 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/PointerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/PointerInterface.json new file mode 100644 index 0000000..c5ae2c8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/PointerInterface.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Reverter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Reverter.json new file mode 100644 index 0000000..ddd6adb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/Reverter.json @@ -0,0 +1,6 @@ +[ + { + "stateMutability": "payable", + "type": "fallback" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/SimpleReadAccessController.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/SimpleReadAccessController.json new file mode 100644 index 0000000..7f5bf2f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/SimpleReadAccessController.json @@ -0,0 +1,188 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/SimpleWriteAccessController.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/SimpleWriteAccessController.json new file mode 100644 index 0000000..396586e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/SimpleWriteAccessController.json @@ -0,0 +1,193 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRF.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRF.json new file mode 100644 index 0000000..d1bdb6d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRF.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "PROOF_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFConsumer.json new file mode 100644 index 0000000..82657f9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFConsumer.json @@ -0,0 +1,86 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "_link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "randomnessOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "requestId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + } + ], + "name": "testRequestRandomness", + "outputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFConsumerBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFConsumerBase.json new file mode 100644 index 0000000..386eeda --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFConsumerBase.json @@ -0,0 +1,36 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "_link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFCoordinator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFCoordinator.json new file mode 100644 index 0000000..ca89bb7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFCoordinator.json @@ -0,0 +1,374 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_blockHashStore", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "NewServiceAgreement", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "jobID", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "requestID", + "type": "bytes32" + } + ], + "name": "RandomnessRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "output", + "type": "uint256" + } + ], + "name": "RandomnessRequestFulfilled", + "type": "event" + }, + { + "inputs": [], + "name": "PRESEED_OFFSET", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROOF_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PUBLIC_KEY_OFFSET", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "callbacks", + "outputs": [ + { + "internalType": "address", + "name": "callbackContract", + "type": "address" + }, + { + "internalType": "uint96", + "name": "randomnessFee", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "seedAndBlockNum", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_proof", + "type": "bytes" + } + ], + "name": "fulfillRandomnessRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "_publicKey", + "type": "uint256[2]" + } + ], + "name": "hashOfKey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_fee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "_publicProvingKey", + "type": "uint256[2]" + }, + { + "internalType": "bytes32", + "name": "_jobID", + "type": "bytes32" + } + ], + "name": "registerProvingKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "serviceAgreements", + "outputs": [ + { + "internalType": "address", + "name": "vRFOracle", + "type": "address" + }, + { + "internalType": "uint96", + "name": "fee", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "jobID", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "withdrawableTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFCoordinatorMock.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFCoordinatorMock.json new file mode 100644 index 0000000..d386885 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFCoordinatorMock.json @@ -0,0 +1,97 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "linkAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "seed", + "type": "uint256" + } + ], + "name": "RandomnessRequest", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + }, + { + "internalType": "address", + "name": "consumerContract", + "type": "address" + } + ], + "name": "callBackWithRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFD20.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFD20.json new file mode 100644 index 0000000..2c5891d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFD20.json @@ -0,0 +1,263 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "result", + "type": "uint256" + } + ], + "name": "DiceLanded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "roller", + "type": "address" + } + ], + "name": "DiceRolled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "player", + "type": "address" + } + ], + "name": "house", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "keyHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "roller", + "type": "address" + } + ], + "name": "rollDice", + "outputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "setFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "setKeyHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "withdrawLINK", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFRequestIDBaseTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFRequestIDBaseTestHelper.json new file mode 100644 index 0000000..53bb38b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFRequestIDBaseTestHelper.json @@ -0,0 +1,60 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_vRFInputSeed", + "type": "uint256" + } + ], + "name": "makeRequestId_", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_userSeed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_requester", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + } + ], + "name": "makeVRFInputSeed_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFTestHelper.json new file mode 100644 index 0000000..e4b24ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/VRFTestHelper.json @@ -0,0 +1,416 @@ +[ + { + "inputs": [], + "name": "PROOF_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "p1", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "p2", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "invZ", + "type": "uint256" + } + ], + "name": "affineECAdd_", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "base", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + } + ], + "name": "bigModExp_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "x", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "scalar", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "q", + "type": "uint256[2]" + } + ], + "name": "ecmulVerify_", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "b", + "type": "bytes" + } + ], + "name": "fieldHash_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "name": "hashToCurve_", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "p1", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "cp1Witness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "p2", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sp2Witness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "name": "linearCombination_", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "px", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "py", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "qx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "qy", + "type": "uint256" + } + ], + "name": "projectiveECAdd_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "name": "randomValueFromVRFProof_", + "outputs": [ + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "hash", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "v", + "type": "uint256[2]" + } + ], + "name": "scalarFromCurvePoints_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "name": "squareRoot_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "p", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lcWitness", + "type": "address" + } + ], + "name": "verifyLinearCombinationWithGenerator_", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "cGammaWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sHashWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "name": "verifyVRFProof_", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "name": "ySquared_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.6/WithdrawalInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/WithdrawalInterface.json new file mode 100644 index 0000000..efdb2d8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.6/WithdrawalInterface.json @@ -0,0 +1,33 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorInterface.json new file mode 100644 index 0000000..7fb29aa --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorInterface.json @@ -0,0 +1,129 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorProxy.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorProxy.json new file mode 100644 index 0000000..45c1902 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorProxy.json @@ -0,0 +1,516 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "aggregatorAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "latest", + "type": "address" + } + ], + "name": "AggregatorConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "AggregatorProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregatorAddress", + "type": "address" + } + ], + "name": "confirmAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregatorAddress", + "type": "address" + } + ], + "name": "proposeAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorProxyInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorProxyInterface.json new file mode 100644 index 0000000..d75128c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorProxyInterface.json @@ -0,0 +1,370 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorV2V3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorV2V3Interface.json new file mode 100644 index 0000000..4667a49 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorV2V3Interface.json @@ -0,0 +1,240 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorV3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorV3Interface.json new file mode 100644 index 0000000..9fe8722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AggregatorV3Interface.json @@ -0,0 +1,113 @@ +[ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedForwarder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedForwarder.json new file mode 100644 index 0000000..2a015f4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedForwarder.json @@ -0,0 +1,268 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "senders", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address", + "name": "changedBy", + "type": "address" + } + ], + "name": "AuthorizedSendersChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "OwnershipTransferRequestedWithMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizedSenders", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "isAuthorizedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ownerForward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "setAuthorizedSenders", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "transferOwnershipWithMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedReceiver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedReceiver.json new file mode 100644 index 0000000..946d5ed --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedReceiver.json @@ -0,0 +1,66 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "senders", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address", + "name": "changedBy", + "type": "address" + } + ], + "name": "AuthorizedSendersChanged", + "type": "event" + }, + { + "inputs": [], + "name": "getAuthorizedSenders", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "isAuthorizedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "setAuthorizedSenders", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedReceiverInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedReceiverInterface.json new file mode 100644 index 0000000..57336ef --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/AuthorizedReceiverInterface.json @@ -0,0 +1,47 @@ +[ + { + "inputs": [], + "name": "getAuthorizedSenders", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "isAuthorizedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "setAuthorizedSenders", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkClient.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkClient.json new file mode 100644 index 0000000..57999bd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkClient.json @@ -0,0 +1,41 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkClientTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkClientTestHelper.json new file mode 100644 index 0000000..cea240f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkClientTestHelper.json @@ -0,0 +1,362 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "LinkAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "callbackfunctionSelector", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "Request", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "fulfillRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "publicAddExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "publicCancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "publicChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "publicFulfillChainlinkRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "publicLINK", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + } + ], + "name": "publicNewRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "publicOracleAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestOracleData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestOracleDataFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestRunTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkRequestInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkRequestInterface.json new file mode 100644 index 0000000..35ea584 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkRequestInterface.json @@ -0,0 +1,78 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestPrice", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "serviceAgreementID", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkTestHelper.json new file mode 100644 index 0000000..225b24d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ChainlinkTestHelper.json @@ -0,0 +1,125 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "payload", + "type": "bytes" + } + ], + "name": "RequestData", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "string", + "name": "_value", + "type": "string" + } + ], + "name": "add", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "bytes", + "name": "_value", + "type": "bytes" + } + ], + "name": "addBytes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "int256", + "name": "_value", + "type": "int256" + } + ], + "name": "addInt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "bytes32[]", + "name": "_values", + "type": "bytes32[]" + } + ], + "name": "addStringArray", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "addUint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "closeEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "setBuffer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/CompoundPriceFlaggingValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/CompoundPriceFlaggingValidator.json new file mode 100644 index 0000000..ed7fafb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/CompoundPriceFlaggingValidator.json @@ -0,0 +1,342 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "compoundOracleAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "CompoundOpenOracleAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "deviationThresholdNumerator", + "type": "uint32" + } + ], + "name": "FeedDetailsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FlagsAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "compoundOpenOracle", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "getFeedDetails", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + }, + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oracleAddress", + "type": "address" + } + ], + "name": "setCompoundOpenOracleAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "internalType": "string", + "name": "compoundSymbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "compoundDecimals", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "compoundDeviationThresholdNumerator", + "type": "uint32" + } + ], + "name": "setFeedDetails", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + } + ], + "name": "setFlagsAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "update", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwner.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwner.json new file mode 100644 index 0000000..73d188c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwner.json @@ -0,0 +1,84 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwnerTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwnerTestHelper.json new file mode 100644 index 0000000..81f35ae --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwnerTestHelper.json @@ -0,0 +1,91 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [], + "name": "Here", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "modifierOnlyOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwnerWithProposal.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwnerWithProposal.json new file mode 100644 index 0000000..db68d10 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ConfirmedOwnerWithProposal.json @@ -0,0 +1,89 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Consumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Consumer.json new file mode 100644 index 0000000..c8d0f28 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Consumer.json @@ -0,0 +1,255 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "price", + "type": "bytes32" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "currentPriceInt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_price", + "type": "bytes32" + } + ], + "name": "fulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_price", + "type": "uint256" + } + ], + "name": "fulfillParametersWithCustomURLs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_urlUSD", + "type": "string" + }, + { + "internalType": "string", + "name": "_pathUSD", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestMultipleParametersWithCustomURLs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "name": "setSpecID", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Denominations.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Denominations.json new file mode 100644 index 0000000..1d16b21 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Denominations.json @@ -0,0 +1,262 @@ +[ + { + "inputs": [], + "name": "ARS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "AUD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BRL", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BTC", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CAD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CHF", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CNY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EUR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GBP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "JPY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "KRW", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NGN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NZD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PHP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RUB", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SGD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "USD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ZAR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/DerivedPriceFeed.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/DerivedPriceFeed.json new file mode 100644 index 0000000..67e570d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/DerivedPriceFeed.json @@ -0,0 +1,287 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_base", + "type": "address" + }, + { + "internalType": "address", + "name": "_quote", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "base", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "quote", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ENSInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ENSInterface.json new file mode 100644 index 0000000..e622e77 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ENSInterface.json @@ -0,0 +1,218 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "label", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ENSResolver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ENSResolver.json new file mode 100644 index 0000000..44ea5ee --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/ENSResolver.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/FeedRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/FeedRegistryInterface.json new file mode 100644 index 0000000..5c017df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/FeedRegistryInterface.json @@ -0,0 +1,803 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "denomination", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "latestAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "previousAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "nextPhaseId", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeedConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "denomination", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "proposedAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "currentAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeedProposed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "confirmFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getCurrentPhaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "currentPhaseId", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getNextRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "nextRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhase", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + }, + { + "internalType": "uint80", + "name": "startingAggregatorRoundId", + "type": "uint80" + }, + { + "internalType": "uint80", + "name": "endingAggregatorRoundId", + "type": "uint80" + } + ], + "internalType": "struct FeedRegistryInterface.Phase", + "name": "phase", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhaseFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhaseRange", + "outputs": [ + { + "internalType": "uint80", + "name": "startingRoundId", + "type": "uint80" + }, + { + "internalType": "uint80", + "name": "endingRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getPreviousRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "previousRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getProposedFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "proposedAggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getRoundFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "isFeedEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "proposeFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/FlagsInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/FlagsInterface.json new file mode 100644 index 0000000..0bba7df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/FlagsInterface.json @@ -0,0 +1,92 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatible.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatible.json new file mode 100644 index 0000000..e934ed8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatible.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatibleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatibleInterface.json new file mode 100644 index 0000000..e934ed8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatibleInterface.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatibleTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatibleTestHelper.json new file mode 100644 index 0000000..adb26a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperCompatibleTestHelper.json @@ -0,0 +1,46 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "testCannotExecute", + "outputs": [], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistry.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistry.json new file mode 100644 index 0000000..9757c80 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistry.json @@ -0,0 +1,1084 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + } + ], + "name": "FlatFeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "RegistrarChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCanceledUpkeepList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFlatFee", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrar", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUpkeepCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "name": "setRegistrar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistry1_1.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistry1_1.json new file mode 100644 index 0000000..9757c80 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistry1_1.json @@ -0,0 +1,1084 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + } + ], + "name": "FlatFeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "RegistrarChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCanceledUpkeepList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFlatFee", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrar", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUpkeepCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "name": "setRegistrar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryBaseInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryBaseInterface.json new file mode 100644 index 0000000..dfdcd04 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryBaseInterface.json @@ -0,0 +1,251 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCanceledUpkeepList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "checkFrequencyBlocks", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUpkeepCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryDev.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryDev.json new file mode 100644 index 0000000..aab9312 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryDev.json @@ -0,0 +1,1529 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "CannotChangePayee", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TooFewKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "RegistrarChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "TranscoderChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepImported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + } + ], + "name": "getPeerRegistryMigrationPermission", + "outputs": [ + { + "internalType": "enum KeeperRegistryDev.MigrationPermission", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "state", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistryDev.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepTranscoderVersion", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryExecutableInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryExecutableInterface.json new file mode 100644 index 0000000..00571b2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryExecutableInterface.json @@ -0,0 +1,295 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCanceledUpkeepList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "checkFrequencyBlocks", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUpkeepCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryInterface.json new file mode 100644 index 0000000..919b9a0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryInterface.json @@ -0,0 +1,295 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "gasWei", + "type": "int256" + }, + { + "internalType": "int256", + "name": "linkEth", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCanceledUpkeepList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "checkFrequencyBlocks", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUpkeepCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryVB.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryVB.json new file mode 100644 index 0000000..4280093 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/KeeperRegistryVB.json @@ -0,0 +1,1113 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "mustTakeTurns", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "mustTakeTurns", + "type": "bool" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + } + ], + "name": "FlatFeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "RegistrarChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCanceledUpkeepList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFlatFee", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMustTakeTurns", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrar", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUpkeepCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "mustTakeTurns", + "type": "bool" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "name": "setRegistrar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/LinkTokenInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/LinkTokenInterface.json new file mode 100644 index 0000000..dc470f1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/LinkTokenInterface.json @@ -0,0 +1,245 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "remaining", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "decimalPlaces", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "totalTokensIssued", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/LinkTokenReceiver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/LinkTokenReceiver.json new file mode 100644 index 0000000..37baafd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/LinkTokenReceiver.json @@ -0,0 +1,38 @@ +[ + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MaliciousMultiWordConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MaliciousMultiWordConsumer.json new file mode 100644 index 0000000..1db0e1d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MaliciousMultiWordConsumer.json @@ -0,0 +1,286 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "assertFail", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "cancelRequestOnFulfill", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "doesNothing", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "guzzleGas", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "maliciousPrice", + "outputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "callbackFunc", + "type": "bytes" + } + ], + "name": "maliciousRequestCancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "remove", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "callbackFunc", + "type": "bytes" + } + ], + "name": "request", + "outputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "callbackFunc", + "type": "bytes" + } + ], + "name": "requestData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "requestGasGuzzle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "stealEthCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "stealEthSend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "stealEthTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MigratableKeeperRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MigratableKeeperRegistryInterface.json new file mode 100644 index 0000000..0a4115d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MigratableKeeperRegistryInterface.json @@ -0,0 +1,46 @@ +[ + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "upkeepIDs", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepTranscoderVersion", + "name": "version", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockCompoundOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockCompoundOracle.json new file mode 100644 index 0000000..0ac15a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockCompoundOracle.json @@ -0,0 +1,44 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "price", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "newPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newDecimals", + "type": "uint256" + } + ], + "name": "setPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockV2Aggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockV2Aggregator.json new file mode 100644 index 0000000..bd621ee --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockV2Aggregator.json @@ -0,0 +1,181 @@ +[ + { + "inputs": [ + { + "internalType": "int256", + "name": "_initialAnswer", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + } + ], + "name": "updateAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_roundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startedAt", + "type": "uint256" + } + ], + "name": "updateRoundData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockV3Aggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockV3Aggregator.json new file mode 100644 index 0000000..bd452b6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MockV3Aggregator.json @@ -0,0 +1,297 @@ +[ + { + "inputs": [ + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "int256", + "name": "_initialAnswer", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + } + ], + "name": "updateAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startedAt", + "type": "uint256" + } + ], + "name": "updateRoundData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MultiWordConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MultiWordConsumer.json new file mode 100644 index 0000000..4e348bc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/MultiWordConsumer.json @@ -0,0 +1,471 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes", + "name": "price", + "type": "bytes" + } + ], + "name": "RequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "usd", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "eur", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "jpy", + "type": "bytes32" + } + ], + "name": "RequestMultipleFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "usd", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "eur", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "jpy", + "type": "uint256" + } + ], + "name": "RequestMultipleFulfilledWithCustomURLs", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "addExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "cancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentPrice", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eur", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eurInt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_price", + "type": "bytes" + } + ], + "name": "fulfillBytes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_usd", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_eur", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_jpy", + "type": "bytes32" + } + ], + "name": "fulfillMultipleParameters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_usd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_eur", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_jpy", + "type": "uint256" + } + ], + "name": "fulfillMultipleParametersWithCustomURLs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "jpy", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "jpyInt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "publicGetNextRequestCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestEthereumPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_currency", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestMultipleParameters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_urlUSD", + "type": "string" + }, + { + "internalType": "string", + "name": "_pathUSD", + "type": "string" + }, + { + "internalType": "string", + "name": "_urlEUR", + "type": "string" + }, + { + "internalType": "string", + "name": "_pathEUR", + "type": "string" + }, + { + "internalType": "string", + "name": "_urlJPY", + "type": "string" + }, + { + "internalType": "string", + "name": "_pathJPY", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + } + ], + "name": "requestMultipleParametersWithCustomURLs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_specId", + "type": "bytes32" + } + ], + "name": "setSpecID", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "usd", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "usdInt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Operator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Operator.json new file mode 100644 index 0000000..5e3b975 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Operator.json @@ -0,0 +1,738 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "senders", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address", + "name": "changedBy", + "type": "address" + } + ], + "name": "AuthorizedSendersChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "name": "CancelOracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "requester", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "callbackAddr", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cancelExpiration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "OracleRequest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + } + ], + "name": "OracleResponse", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "acceptedContract", + "type": "address" + } + ], + "name": "OwnableContractAccepted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "senders", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address", + "name": "changedBy", + "type": "address" + } + ], + "name": "TargetsUpdatedAuthorizedSenders", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "acceptAuthorizedReceivers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "ownable", + "type": "address[]" + } + ], + "name": "acceptOwnableContracts", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunc", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunc", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequestByRequester", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable[]", + "name": "receivers", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "distributeFunds", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "fulfillOracleRequest2", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizedSenders", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExpiryTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "isAuthorizedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "operatorRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ownerForward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ownerTransferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "setAuthorizedSenders", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "setAuthorizedSendersOn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "ownable", + "type": "address[]" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnableContracts", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OperatorFactory.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OperatorFactory.json new file mode 100644 index 0000000..47195ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OperatorFactory.json @@ -0,0 +1,176 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "linkAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "forwarder", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "AuthorizedForwarderCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "OperatorCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "created", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "deployNewForwarder", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "deployNewForwarderAndTransferOwnership", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deployNewOperator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deployNewOperatorAndForwarder", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OperatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OperatorInterface.json new file mode 100644 index 0000000..191e941 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OperatorInterface.json @@ -0,0 +1,269 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "fulfillOracleRequest2", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "operatorRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestPrice", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "serviceAgreementID", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ownerTransferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OracleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OracleInterface.json new file mode 100644 index 0000000..d74508e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OracleInterface.json @@ -0,0 +1,77 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OwnableInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OwnableInterface.json new file mode 100644 index 0000000..ee9d817 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/OwnableInterface.json @@ -0,0 +1,35 @@ +[ + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Pausable.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Pausable.json new file mode 100644 index 0000000..9852560 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/Pausable.json @@ -0,0 +1,41 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/PointerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/PointerInterface.json new file mode 100644 index 0000000..c5ae2c8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/PointerInterface.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/StalenessFlaggingValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/StalenessFlaggingValidator.json new file mode 100644 index 0000000..d90a73f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/StalenessFlaggingValidator.json @@ -0,0 +1,266 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "previous", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "current", + "type": "uint256" + } + ], + "name": "FlaggingThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "FlagsAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + } + ], + "name": "setFlagsAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "flaggingThresholds", + "type": "uint256[]" + } + ], + "name": "setThresholds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "threshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "update", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/TypeAndVersionInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/TypeAndVersionInterface.json new file mode 100644 index 0000000..99725a5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/TypeAndVersionInterface.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UniswapAnchoredView.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UniswapAnchoredView.json new file mode 100644 index 0000000..0f5eff3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UniswapAnchoredView.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "price", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepAutoFunder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepAutoFunder.json new file mode 100644 index 0000000..b352cd0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepAutoFunder.json @@ -0,0 +1,256 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "linkAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "registryAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "callable", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "executedata", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_autoFundLink", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_isEligible", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_keeperRegistry", + "outputs": [ + { + "internalType": "contract KeeperRegistryBaseInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_shouldCancel", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_upkeepId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "value", + "type": "uint96" + } + ], + "name": "setAutoFundLink", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "value", + "type": "bool" + } + ], + "name": "setIsEligible", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "value", + "type": "bool" + } + ], + "name": "setShouldCancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setUpkeepId", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepCounter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepCounter.json new file mode 100644 index 0000000..1ce9687 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepCounter.json @@ -0,0 +1,201 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_testRange", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_interval", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialBlock", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lastBlock", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousBlock", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "counter", + "type": "uint256" + } + ], + "name": "PerformingUpkeep", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "counter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eligible", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "interval", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "previousPerformBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_testRange", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_interval", + "type": "uint256" + } + ], + "name": "setSpread", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "testRange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepMock.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepMock.json new file mode 100644 index 0000000..c56e1cd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepMock.json @@ -0,0 +1,156 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "upkeepData", + "type": "bytes" + } + ], + "name": "UpkeepPerformedWith", + "type": "event" + }, + { + "inputs": [], + "name": "canCheck", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "canPerform", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkGasToBurn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "callable", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "executedata", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "performGasToBurn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "value", + "type": "bool" + } + ], + "name": "setCanCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "value", + "type": "bool" + } + ], + "name": "setCanPerform", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setCheckGasToBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setPerformGasToBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepPerformCounterRestrictive.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepPerformCounterRestrictive.json new file mode 100644 index 0000000..a6d3e16 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepPerformCounterRestrictive.json @@ -0,0 +1,266 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_testRange", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_averageEligibilityCadence", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "eligible", + "type": "bool" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialCall", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nextEligible", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "PerformingUpkeep", + "type": "event" + }, + { + "inputs": [], + "name": "averageEligibilityCadence", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEligible", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkGasToBurn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "dummyMap", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCountPerforms", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialCall", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextEligible", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "performGasToBurn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setCheckGasToBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "setPerformGasToBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newTestRange", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_newAverageEligibilityCadence", + "type": "uint256" + } + ], + "name": "setSpread", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "testRange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepRegistrationRequests.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepRegistrationRequests.json new file mode 100644 index 0000000..a09ad6c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepRegistrationRequests.json @@ -0,0 +1,473 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "LINKAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minimumLINKJuels", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "windowSizeInBlocks", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "allowedPerWindow", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "minLINKJuels", + "type": "uint256" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "displayName", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + } + ], + "name": "RegistrationApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "RegistrationRejected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "source", + "type": "uint8" + } + ], + "name": "RegistrationRequested", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "cancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "getPendingRequest", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrationConfig", + "outputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "windowSizeInBlocks", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "allowedPerWindow", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minLINKJuels", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "windowStart", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "approvedInCurrentWindow", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "internalType": "uint8", + "name": "source", + "type": "uint8" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "windowSizeInBlocks", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "allowedPerWindow", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minLINKJuels", + "type": "uint256" + } + ], + "name": "setRegistrationConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepReverter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepReverter.json new file mode 100644 index 0000000..aebd173 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepReverter.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "callable", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "executedata", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepTranscoderInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepTranscoderInterface.json new file mode 100644 index 0000000..f130f05 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/UpkeepTranscoderInterface.json @@ -0,0 +1,31 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "fromRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "toRegistry", + "type": "address" + }, + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "transcodeUpkeeps", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/VRFConsumerBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/VRFConsumerBase.json new file mode 100644 index 0000000..2f5c323 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/VRFConsumerBase.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/VRFCoordinatorMock.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/VRFCoordinatorMock.json new file mode 100644 index 0000000..d386885 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/VRFCoordinatorMock.json @@ -0,0 +1,97 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "linkAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "seed", + "type": "uint256" + } + ], + "name": "RandomnessRequest", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + }, + { + "internalType": "address", + "name": "consumerContract", + "type": "address" + } + ], + "name": "callBackWithRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.7/WithdrawalInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/WithdrawalInterface.json new file mode 100644 index 0000000..efdb2d8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.7/WithdrawalInterface.json @@ -0,0 +1,33 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AccessControllerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AccessControllerInterface.json new file mode 100644 index 0000000..d018c95 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AccessControllerInterface.json @@ -0,0 +1,26 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorInterface.json new file mode 100644 index 0000000..7fb29aa --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorInterface.json @@ -0,0 +1,129 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorProxy.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorProxy.json new file mode 100644 index 0000000..45c1902 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorProxy.json @@ -0,0 +1,516 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "aggregatorAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "latest", + "type": "address" + } + ], + "name": "AggregatorConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "AggregatorProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregatorAddress", + "type": "address" + } + ], + "name": "confirmAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregatorAddress", + "type": "address" + } + ], + "name": "proposeAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorProxyInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorProxyInterface.json new file mode 100644 index 0000000..d75128c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorProxyInterface.json @@ -0,0 +1,370 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "aggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "phaseAggregators", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "phaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedAggregator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorV2V3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorV2V3Interface.json new file mode 100644 index 0000000..4667a49 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorV2V3Interface.json @@ -0,0 +1,240 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorV3Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorV3Interface.json new file mode 100644 index 0000000..9fe8722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorV3Interface.json @@ -0,0 +1,113 @@ +[ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorValidatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorValidatorInterface.json new file mode 100644 index 0000000..54a6a92 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AggregatorValidatorInterface.json @@ -0,0 +1,36 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "previousRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "currentRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbGasInfo.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbGasInfo.json new file mode 100644 index 0000000..e51530a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbGasInfo.json @@ -0,0 +1,198 @@ +[ + { + "inputs": [], + "name": "getCurrentTxL1GasFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGasAccountingParams", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getL1GasPriceEstimate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPricesInArbGas", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "getPricesInArbGasWithAggregator", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPricesInWei", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "getPricesInWeiWithAggregator", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "priceInWei", + "type": "uint256" + } + ], + "name": "setL1GasPriceEstimate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbSys.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbSys.json new file mode 100644 index 0000000..dcf26b1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbSys.json @@ -0,0 +1,207 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "uniqueId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "batchNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "indexInBatch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "arbBlockNum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethBlockNum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "callvalue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "L2ToL1Transaction", + "type": "event" + }, + { + "inputs": [], + "name": "arbBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "arbChainID", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "arbOSVersion", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getStorageAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getTransactionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isTopLevelCall", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + }, + { + "internalType": "bytes", + "name": "calldataForL1", + "type": "bytes" + } + ], + "name": "sendTxToL1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "withdrawEth", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumCrossDomainForwarder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumCrossDomainForwarder.json new file mode 100644 index 0000000..b6f66e0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumCrossDomainForwarder.json @@ -0,0 +1,199 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "l1OwnerAddr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "crossDomainMessenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumCrossDomainGovernor.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumCrossDomainGovernor.json new file mode 100644 index 0000000..6df7093 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumCrossDomainGovernor.json @@ -0,0 +1,217 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "l1OwnerAddr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "crossDomainMessenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forwardDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumSequencerUptimeFeed.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumSequencerUptimeFeed.json new file mode 100644 index 0000000..9c568b8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumSequencerUptimeFeed.json @@ -0,0 +1,621 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "l1SenderAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSender", + "type": "error" + }, + { + "inputs": [], + "name": "NoDataPresent", + "type": "error" + }, + { + "inputs": [], + "name": "Uninitialized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1SenderTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "latestStatus", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "latestTimestamp", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "bool", + "name": "incomingStatus", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "incomingTimestamp", + "type": "uint64" + } + ], + "name": "UpdateIgnored", + "type": "event" + }, + { + "inputs": [], + "name": "FLAGS", + "outputs": [ + { + "internalType": "contract FlagsInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FLAG_L2_SEQ_OFFLINE", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "aliasedL1MessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Sender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Sender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "status", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "name": "updateStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumSequencerUptimeFeedInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumSequencerUptimeFeedInterface.json new file mode 100644 index 0000000..a67bc9d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumSequencerUptimeFeedInterface.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "bool", + "name": "status", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "name": "updateStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumValidator.json new file mode 100644 index 0000000..a678edd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ArbitrumValidator.json @@ -0,0 +1,565 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "crossDomainMessengerAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "l2ArbitrumSequencerUptimeFeedAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "configACAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gasPriceL1FeedAddr", + "type": "address" + }, + { + "internalType": "enum ArbitrumValidator.PaymentStrategy", + "name": "paymentStrategy", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "ConfigACSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "gasPriceL1FeedAddr", + "type": "address" + } + ], + "name": "GasConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "refundAddr", + "type": "address" + } + ], + "name": "L2WithdrawalRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum ArbitrumValidator.PaymentStrategy", + "name": "paymentStrategy", + "type": "uint8" + } + ], + "name": "PaymentStrategySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "CROSS_DOMAIN_MESSENGER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L2_ALIAS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L2_SEQ_STATUS_RECORDER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "configAC", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "gasConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gasPriceL1FeedAddr", + "type": "address" + } + ], + "internalType": "struct ArbitrumValidator.GasConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paymentStrategy", + "outputs": [ + { + "internalType": "enum ArbitrumValidator.PaymentStrategy", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessController", + "type": "address" + } + ], + "name": "setConfigAC", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseFee", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gasPriceL1FeedAddr", + "type": "address" + } + ], + "name": "setGasConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ArbitrumValidator.PaymentStrategy", + "name": "paymentStrategy", + "type": "uint8" + } + ], + "name": "setPaymentStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "refundAddr", + "type": "address" + } + ], + "name": "withdrawFundsFromL2", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "recipient", + "type": "address" + } + ], + "name": "withdrawFundsTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationBase.json new file mode 100644 index 0000000..b544978 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationBase.json @@ -0,0 +1,7 @@ +[ + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationCompatible.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationCompatible.json new file mode 100644 index 0000000..aae2720 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationCompatible.json @@ -0,0 +1,44 @@ +[ + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationCompatibleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationCompatibleInterface.json new file mode 100644 index 0000000..e934ed8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationCompatibleInterface.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryBaseInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryBaseInterface.json new file mode 100644 index 0000000..6598f64 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryBaseInterface.json @@ -0,0 +1,323 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryExecutableInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryExecutableInterface.json new file mode 100644 index 0000000..36b9515 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryExecutableInterface.json @@ -0,0 +1,367 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryInterface.json new file mode 100644 index 0000000..784dfee --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/AutomationRegistryInterface.json @@ -0,0 +1,367 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "gasWei", + "type": "int256" + }, + { + "internalType": "int256", + "name": "linkEth", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BatchBlockhashStore.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BatchBlockhashStore.json new file mode 100644 index 0000000..0c7bfe4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BatchBlockhashStore.json @@ -0,0 +1,76 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "blockhashStoreAddr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "BHS", + "outputs": [ + { + "internalType": "contract BlockhashStore", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "blockNumbers", + "type": "uint256[]" + } + ], + "name": "getBlockhashes", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "blockNumbers", + "type": "uint256[]" + } + ], + "name": "store", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "blockNumbers", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "headers", + "type": "bytes[]" + } + ], + "name": "storeVerifyHeader", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BatchVRFCoordinatorV2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BatchVRFCoordinatorV2.json new file mode 100644 index 0000000..9a97cfa --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BatchVRFCoordinatorV2.json @@ -0,0 +1,156 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "coordinatorAddr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "ErrorReturned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "lowLevelData", + "type": "bytes" + } + ], + "name": "RawErrorReturned", + "type": "event" + }, + { + "inputs": [], + "name": "COORDINATOR", + "outputs": [ + { + "internalType": "contract VRFCoordinatorV2", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "cGammaWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sHashWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "internalType": "struct VRFTypes.Proof[]", + "name": "proofs", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "blockNum", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "internalType": "struct VRFTypes.RequestCommitment[]", + "name": "rcs", + "type": "tuple[]" + } + ], + "name": "fulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BlockhashStore.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BlockhashStore.json new file mode 100644 index 0000000..fdf9512 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BlockhashStore.json @@ -0,0 +1,52 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "getBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + } + ], + "name": "store", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "n", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "header", + "type": "bytes" + } + ], + "name": "storeVerifyHeader", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BlockhashStoreInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BlockhashStoreInterface.json new file mode 100644 index 0000000..7e9ac19 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/BlockhashStoreInterface.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + } + ], + "name": "getBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Broken.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Broken.json new file mode 100644 index 0000000..8b54d23 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Broken.json @@ -0,0 +1,45 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "reason", + "type": "string" + }, + { + "internalType": "int256", + "name": "reason2", + "type": "int256" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "revertSilently", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "revertWithCustomError", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "revertWithMessage", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CanaryUpkeep.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CanaryUpkeep.json new file mode 100644 index 0000000..3cc663e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CanaryUpkeep.json @@ -0,0 +1,201 @@ +[ + { + "inputs": [ + { + "internalType": "contract KeeperRegistryExecutableInterface", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "interval", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InsufficientInterval", + "type": "error" + }, + { + "inputs": [], + "name": "NoKeeperNodes", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getInterval", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperRegistry", + "outputs": [ + { + "internalType": "contract KeeperRegistryExecutableInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "interval", + "type": "uint256" + } + ], + "name": "setInterval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CanaryUpkeep1_2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CanaryUpkeep1_2.json new file mode 100644 index 0000000..7bbde82 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CanaryUpkeep1_2.json @@ -0,0 +1,201 @@ +[ + { + "inputs": [ + { + "internalType": "contract AutomationRegistryExecutableInterface", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "interval", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InsufficientInterval", + "type": "error" + }, + { + "inputs": [], + "name": "NoKeeperNodes", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getInterval", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperRegistry", + "outputs": [ + { + "internalType": "contract AutomationRegistryExecutableInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "interval", + "type": "uint256" + } + ], + "name": "setInterval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkClient.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkClient.json new file mode 100644 index 0000000..57999bd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkClient.json @@ -0,0 +1,41 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkClientTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkClientTestHelper.json new file mode 100644 index 0000000..14bfa8f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkClientTestHelper.json @@ -0,0 +1,357 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_oracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "ChainlinkRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "LinkAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "callbackfunctionSelector", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "Request", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "fulfillRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + } + ], + "name": "publicAddExternalRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "_callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "publicCancelRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "publicChainlinkToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_requestId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "publicFulfillChainlinkRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "publicLINK", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + } + ], + "name": "publicNewRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "publicOracleAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestOracleData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestOracleDataFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_oracle", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_fulfillmentSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_wei", + "type": "uint256" + } + ], + "name": "publicRequestRunTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkRequestInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkRequestInterface.json new file mode 100644 index 0000000..35ea584 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkRequestInterface.json @@ -0,0 +1,78 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestPrice", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "serviceAgreementID", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkTestHelper.json new file mode 100644 index 0000000..7470682 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ChainlinkTestHelper.json @@ -0,0 +1,125 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "payload", + "type": "bytes" + } + ], + "name": "RequestData", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "string", + "name": "_value", + "type": "string" + } + ], + "name": "add", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "bytes", + "name": "_value", + "type": "bytes" + } + ], + "name": "addBytes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "int256", + "name": "_value", + "type": "int256" + } + ], + "name": "addInt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "string[]", + "name": "_values", + "type": "string[]" + } + ], + "name": "addStringArray", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_key", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "addUint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "closeEvent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "setBuffer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CompoundPriceFlaggingValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CompoundPriceFlaggingValidator.json new file mode 100644 index 0000000..ed7fafb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CompoundPriceFlaggingValidator.json @@ -0,0 +1,342 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "compoundOracleAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "CompoundOpenOracleAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "deviationThresholdNumerator", + "type": "uint32" + } + ], + "name": "FeedDetailsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FlagsAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "compoundOpenOracle", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "getFeedDetails", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + }, + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oracleAddress", + "type": "address" + } + ], + "name": "setCompoundOpenOracleAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "internalType": "string", + "name": "compoundSymbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "compoundDecimals", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "compoundDeviationThresholdNumerator", + "type": "uint32" + } + ], + "name": "setFeedDetails", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + } + ], + "name": "setFlagsAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "update", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ConfirmedOwner.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ConfirmedOwner.json new file mode 100644 index 0000000..73d188c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ConfirmedOwner.json @@ -0,0 +1,84 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ConfirmedOwnerWithProposal.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ConfirmedOwnerWithProposal.json new file mode 100644 index 0000000..db68d10 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ConfirmedOwnerWithProposal.json @@ -0,0 +1,89 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Counter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Counter.json new file mode 100644 index 0000000..1d93c6e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Counter.json @@ -0,0 +1,54 @@ +[ + { + "inputs": [], + "name": "AlwaysRevert", + "type": "error" + }, + { + "inputs": [], + "name": "alwaysRevert", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "alwaysRevertWithString", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "count", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "increment", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Cron.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Cron.json new file mode 100644 index 0000000..1af3931 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Cron.json @@ -0,0 +1,1218 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "field", + "type": "string" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidField", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidSpec", + "type": "error" + }, + { + "inputs": [], + "name": "ListTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownFieldType", + "type": "error" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "minute", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "hour", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "day", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "month", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "dayOfWeek", + "type": "tuple" + } + ], + "internalType": "struct Spec", + "name": "spec", + "type": "tuple" + } + ], + "name": "lastTick", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "minute", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "hour", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "day", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "month", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "dayOfWeek", + "type": "tuple" + } + ], + "internalType": "struct Spec", + "name": "spec", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "matches", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "minute", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "hour", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "day", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "month", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "dayOfWeek", + "type": "tuple" + } + ], + "internalType": "struct Spec", + "name": "spec", + "type": "tuple" + } + ], + "name": "nextTick", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "minute", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "hour", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "day", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "month", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "dayOfWeek", + "type": "tuple" + } + ], + "internalType": "struct Spec", + "name": "spec", + "type": "tuple" + } + ], + "name": "toCronString", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "toEncodedSpec", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "toSpec", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "minute", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "hour", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "day", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "month", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum FieldType", + "name": "fieldType", + "type": "FieldType" + }, + { + "internalType": "uint8", + "name": "singleValue", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "interval", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeStart", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "rangeEnd", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "listLength", + "type": "uint8" + }, + { + "internalType": "uint8[26]", + "name": "list", + "type": "uint8[26]" + } + ], + "internalType": "struct Field", + "name": "dayOfWeek", + "type": "tuple" + } + ], + "internalType": "struct Spec", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronExternalTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronExternalTestHelper.json new file mode 100644 index 0000000..3db6c40 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronExternalTestHelper.json @@ -0,0 +1,78 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "calculateLastTick", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "calculateNextTick", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "encodeCronString", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedSpec", + "type": "bytes" + } + ], + "name": "encodedSpecToString", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronInternalTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronInternalTestHelper.json new file mode 100644 index 0000000..e7e643b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronInternalTestHelper.json @@ -0,0 +1,115 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "field", + "type": "string" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidField", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidSpec", + "type": "error" + }, + { + "inputs": [], + "name": "ListTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownFieldType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "calculateLastTick", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "calculateNextTick", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "encodeCronString", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedSpec", + "type": "bytes" + } + ], + "name": "encodedSpecToString", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronReceiver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronReceiver.json new file mode 100644 index 0000000..b08bb5d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronReceiver.json @@ -0,0 +1,35 @@ +[ + { + "anonymous": false, + "inputs": [], + "name": "Received1", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "Received2", + "type": "event" + }, + { + "inputs": [], + "name": "handler1", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "handler2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "revertHandler", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeep.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeep.json new file mode 100644 index 0000000..aa40fec --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeep.json @@ -0,0 +1,465 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxJobs", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "firstJob", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "CallFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "CronJobIDNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "ExceedsMaxJobs", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidHandler", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "TickDoesntMatchSpec", + "type": "error" + }, + { + "inputs": [], + "name": "TickInFuture", + "type": "error" + }, + { + "inputs": [], + "name": "TickTooOld", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownFieldType", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "handler", + "type": "bytes" + } + ], + "name": "CronJobCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "CronJobDeleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "CronJobExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "handler", + "type": "bytes" + } + ], + "name": "CronJobUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "encodedCronSpec", + "type": "bytes" + } + ], + "name": "createCronJobFromEncodedSpec", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "deleteCronJob", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getActiveCronJobIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getCronJob", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + }, + { + "internalType": "string", + "name": "cronString", + "type": "string" + }, + { + "internalType": "uint256", + "name": "nextTick", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_maxJobs", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "newTarget", + "type": "address" + }, + { + "internalType": "bytes", + "name": "newHandler", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "newEncodedCronSpec", + "type": "bytes" + } + ], + "name": "updateCronJob", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepDelegate.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepDelegate.json new file mode 100644 index 0000000..46baaac --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepDelegate.json @@ -0,0 +1,31 @@ +[ + { + "inputs": [], + "name": "UnknownFieldType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepFactory.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepFactory.json new file mode 100644 index 0000000..3c413d9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepFactory.json @@ -0,0 +1,204 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "upkeep", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "NewCronUpkeepCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cronDelegateAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + }, + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "encodeCronJob", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "encodeCronString", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "newCronUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedJob", + "type": "bytes" + } + ], + "name": "newCronUpkeepWithJob", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_maxJobs", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxJobs", + "type": "uint256" + } + ], + "name": "setMaxJobs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepTestHelper.json new file mode 100644 index 0000000..0ed5461 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CronUpkeepTestHelper.json @@ -0,0 +1,533 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxJobs", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "firstJob", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "CallFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "CronJobIDNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "ExceedsMaxJobs", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "field", + "type": "string" + }, + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidField", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidHandler", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "InvalidSpec", + "type": "error" + }, + { + "inputs": [], + "name": "ListTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "TickDoesntMatchSpec", + "type": "error" + }, + { + "inputs": [], + "name": "TickInFuture", + "type": "error" + }, + { + "inputs": [], + "name": "TickTooOld", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownFieldType", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "handler", + "type": "bytes" + } + ], + "name": "CronJobCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "CronJobDeleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "CronJobExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "handler", + "type": "bytes" + } + ], + "name": "CronJobUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "encodedCronSpec", + "type": "bytes" + } + ], + "name": "createCronJobFromEncodedSpec", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + }, + { + "internalType": "string", + "name": "cronString", + "type": "string" + } + ], + "name": "createCronJobFromString", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "deleteCronJob", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getActiveCronJobIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getCronJob", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + }, + { + "internalType": "string", + "name": "cronString", + "type": "string" + }, + { + "internalType": "uint256", + "name": "nextTick", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_maxJobs", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "txCheckUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "newTarget", + "type": "address" + }, + { + "internalType": "bytes", + "name": "newHandler", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "newEncodedCronSpec", + "type": "bytes" + } + ], + "name": "updateCronJob", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainDelegateForwarder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainDelegateForwarder.json new file mode 100644 index 0000000..d64def7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainDelegateForwarder.json @@ -0,0 +1,162 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forwardDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainForwarder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainForwarder.json new file mode 100644 index 0000000..693d75c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainForwarder.json @@ -0,0 +1,162 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainOwnable.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainOwnable.json new file mode 100644 index 0000000..1e093a2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainOwnable.json @@ -0,0 +1,155 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "newl1Owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainOwnableInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainOwnableInterface.json new file mode 100644 index 0000000..663cd86 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/CrossDomainOwnableInterface.json @@ -0,0 +1,73 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DelegateForwarderInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DelegateForwarderInterface.json new file mode 100644 index 0000000..10f01bf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DelegateForwarderInterface.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forwardDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Denominations.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Denominations.json new file mode 100644 index 0000000..1d16b21 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Denominations.json @@ -0,0 +1,262 @@ +[ + { + "inputs": [], + "name": "ARS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "AUD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BRL", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BTC", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CAD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CHF", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CNY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EUR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GBP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "JPY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "KRW", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NGN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NZD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PHP", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RUB", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SGD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "USD", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ZAR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DerivedPriceFeed.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DerivedPriceFeed.json new file mode 100644 index 0000000..0ff78dd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DerivedPriceFeed.json @@ -0,0 +1,173 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_base", + "type": "address" + }, + { + "internalType": "address", + "name": "_quote", + "type": "address" + }, + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "BASE", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DECIMALS", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "QUOTE", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DiskHeavyUpkeep.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DiskHeavyUpkeep.json new file mode 100644 index 0000000..d17ce0c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/DiskHeavyUpkeep.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ENSInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ENSInterface.json new file mode 100644 index 0000000..e622e77 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ENSInterface.json @@ -0,0 +1,218 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "label", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "NewOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "NewResolver", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "NewTTL", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "resolver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "label", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "setSubnodeOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "ttl", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ENSResolver.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ENSResolver.json new file mode 100644 index 0000000..44ea5ee --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ENSResolver.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "addr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ERC677ReceiverInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ERC677ReceiverInterface.json new file mode 100644 index 0000000..fd79f59 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ERC677ReceiverInterface.json @@ -0,0 +1,25 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/EthBalanceMonitor.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/EthBalanceMonitor.json new file mode 100644 index 0000000..67ccd39 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/EthBalanceMonitor.json @@ -0,0 +1,478 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "keeperRegistryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minWaitPeriodSeconds", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "duplicate", + "type": "address" + } + ], + "name": "DuplicateAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidWatchList", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyKeeperRegistry", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amountAdded", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amountWithdrawn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "KeeperRegistryAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMinWaitPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newMinWaitPeriod", + "type": "uint256" + } + ], + "name": "MinWaitPeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "TopUpFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "TopUpSucceeded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "targetAddress", + "type": "address" + } + ], + "name": "getAccountInfo", + "outputs": [ + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "minBalanceWei", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "topUpAmountWei", + "type": "uint96" + }, + { + "internalType": "uint56", + "name": "lastTopUpTimestamp", + "type": "uint56" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperRegistryAddress", + "outputs": [ + { + "internalType": "address", + "name": "keeperRegistryAddress", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinWaitPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUnderfundedAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getWatchList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeperRegistryAddress", + "type": "address" + } + ], + "name": "setKeeperRegistryAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "period", + "type": "uint256" + } + ], + "name": "setMinWaitPeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "addresses", + "type": "address[]" + }, + { + "internalType": "uint96[]", + "name": "minBalancesWei", + "type": "uint96[]" + }, + { + "internalType": "uint96[]", + "name": "topUpAmountsWei", + "type": "uint96[]" + } + ], + "name": "setWatchList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "needsFunding", + "type": "address[]" + } + ], + "name": "topUp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address payable", + "name": "payee", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/EthBalanceMonitorExposed.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/EthBalanceMonitorExposed.json new file mode 100644 index 0000000..d7114e3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/EthBalanceMonitorExposed.json @@ -0,0 +1,496 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "keeperRegistryAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minWaitPeriod", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "duplicate", + "type": "address" + } + ], + "name": "DuplicateAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidWatchList", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyKeeperRegistry", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amountAdded", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amountWithdrawn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAddress", + "type": "address" + } + ], + "name": "KeeperRegistryAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMinWaitPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newMinWaitPeriod", + "type": "uint256" + } + ], + "name": "MinWaitPeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "TopUpFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "TopUpSucceeded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "targetAddress", + "type": "address" + } + ], + "name": "getAccountInfo", + "outputs": [ + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "minBalanceWei", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "topUpAmountWei", + "type": "uint96" + }, + { + "internalType": "uint56", + "name": "lastTopUpTimestamp", + "type": "uint56" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperRegistryAddress", + "outputs": [ + { + "internalType": "address", + "name": "keeperRegistryAddress", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinWaitPeriodSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUnderfundedAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getWatchList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeperRegistryAddress", + "type": "address" + } + ], + "name": "setKeeperRegistryAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint56", + "name": "lastTopUpTimestamp", + "type": "uint56" + } + ], + "name": "setLastTopUpXXXTestOnly", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "period", + "type": "uint256" + } + ], + "name": "setMinWaitPeriodSeconds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "addresses", + "type": "address[]" + }, + { + "internalType": "uint96[]", + "name": "minBalancesWei", + "type": "uint96[]" + }, + { + "internalType": "uint96[]", + "name": "topUpAmountsWei", + "type": "uint96[]" + } + ], + "name": "setWatchList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "needsFunding", + "type": "address[]" + } + ], + "name": "topUp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address payable", + "name": "payee", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ExecutionPrevention.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ExecutionPrevention.json new file mode 100644 index 0000000..b544978 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ExecutionPrevention.json @@ -0,0 +1,7 @@ +[ + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ExtendedVRFCoordinatorV2Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ExtendedVRFCoordinatorV2Interface.json new file mode 100644 index 0000000..8e08d03 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ExtendedVRFCoordinatorV2Interface.json @@ -0,0 +1,309 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "acceptSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "addConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "cancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "createSubscription", + "outputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFallbackWeiPerUnitLink", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRequestConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "getSubscription", + "outputs": [ + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "pendingRequestExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "removeConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + } + ], + "name": "requestRandomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "requestSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FeedConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FeedConsumer.json new file mode 100644 index 0000000..6e3c561 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FeedConsumer.json @@ -0,0 +1,214 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "feedAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AGGREGATOR", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FeedRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FeedRegistryInterface.json new file mode 100644 index 0000000..5c017df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FeedRegistryInterface.json @@ -0,0 +1,803 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "denomination", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "latestAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "previousAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "nextPhaseId", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeedConfirmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "denomination", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "proposedAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "currentAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeedProposed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "confirmFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getCurrentPhaseId", + "outputs": [ + { + "internalType": "uint16", + "name": "currentPhaseId", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getNextRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "nextRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhase", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + }, + { + "internalType": "uint80", + "name": "startingAggregatorRoundId", + "type": "uint80" + }, + { + "internalType": "uint80", + "name": "endingAggregatorRoundId", + "type": "uint80" + } + ], + "internalType": "struct FeedRegistryInterface.Phase", + "name": "phase", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhaseFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint16", + "name": "phaseId", + "type": "uint16" + } + ], + "name": "getPhaseRange", + "outputs": [ + { + "internalType": "uint80", + "name": "startingRoundId", + "type": "uint80" + }, + { + "internalType": "uint80", + "name": "endingRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getPreviousRoundId", + "outputs": [ + { + "internalType": "uint80", + "name": "previousRoundId", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "getProposedFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "proposedAggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "getRoundFeed", + "outputs": [ + { + "internalType": "contract AggregatorV2V3Interface", + "name": "aggregator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "isFeedEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "proposeFeed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + }, + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + } + ], + "name": "proposedGetRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "proposedLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "id", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "base", + "type": "address" + }, + { + "internalType": "address", + "name": "quote", + "type": "address" + } + ], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Flags.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Flags.json new file mode 100644 index 0000000..e9d61b4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Flags.json @@ -0,0 +1,347 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "racAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "FlagLowered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "FlagRaised", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "RaisingAccessControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "raisingAccessController", + "outputs": [ + { + "internalType": "contract AccessControllerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "racAddress", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FlagsInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FlagsInterface.json new file mode 100644 index 0000000..0bba7df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FlagsInterface.json @@ -0,0 +1,92 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "lowerFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "raiseFlag", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "name": "raiseFlags", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "setRaisingAccessController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FlagsTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FlagsTestHelper.json new file mode 100644 index 0000000..f42ff6d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/FlagsTestHelper.json @@ -0,0 +1,64 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsContract", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "contract Flags", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "subject", + "type": "address" + } + ], + "name": "getFlag", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "subjects", + "type": "address[]" + } + ], + "name": "getFlags", + "outputs": [ + { + "internalType": "bool[]", + "name": "", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ForwarderInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ForwarderInterface.json new file mode 100644 index 0000000..301144c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ForwarderInterface.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Greeter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Greeter.json new file mode 100644 index 0000000..edf4dea --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/Greeter.json @@ -0,0 +1,117 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "greeting", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_greeting", + "type": "string" + } + ], + "name": "setGreeting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "triggerRevert", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IArbitrumDelayedInbox.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IArbitrumDelayedInbox.json new file mode 100644 index 0000000..086ef65 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IArbitrumDelayedInbox.json @@ -0,0 +1,407 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "InboxMessageDelivered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + } + ], + "name": "InboxMessageDeliveredFromOrigin", + "type": "event" + }, + { + "inputs": [], + "name": "bridge", + "outputs": [ + { + "internalType": "contract IBridge", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "dataLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseFee", + "type": "uint256" + } + ], + "name": "calculateRetryableSubmissionFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createRetryableTicket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createRetryableTicketNoRefundAliasRewrite", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + } + ], + "name": "depositEth", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGasPrice", + "type": "uint256" + } + ], + "name": "depositEthRetryable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendContractTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendL1FundedContractTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendL1FundedUnsignedTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "messageData", + "type": "bytes" + } + ], + "name": "sendL2Message", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendUnsignedTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IBridge.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IBridge.json new file mode 100644 index 0000000..31fc615 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IBridge.json @@ -0,0 +1,227 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "beforeInboxAcc", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "inbox", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "kind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "messageDataHash", + "type": "bytes32" + } + ], + "name": "MessageDelivered", + "type": "event" + }, + { + "inputs": [], + "name": "activeOutbox", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "inbox", + "type": "address" + } + ], + "name": "allowedInboxes", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "outbox", + "type": "address" + } + ], + "name": "allowedOutboxes", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "messageDataHash", + "type": "bytes32" + } + ], + "name": "deliverMessageToInbox", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "executeCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "inboxAccs", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "messageCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "inbox", + "type": "address" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setInbox", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "inbox", + "type": "address" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setOutbox", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IInbox.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IInbox.json new file mode 100644 index 0000000..aa899e6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IInbox.json @@ -0,0 +1,383 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "InboxMessageDelivered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + } + ], + "name": "InboxMessageDeliveredFromOrigin", + "type": "event" + }, + { + "inputs": [], + "name": "bridge", + "outputs": [ + { + "internalType": "contract IBridge", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createRetryableTicket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createRetryableTicketNoRefundAliasRewrite", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + } + ], + "name": "depositEth", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGasPrice", + "type": "uint256" + } + ], + "name": "depositEthRetryable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendContractTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendL1FundedContractTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendL1FundedUnsignedTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "messageData", + "type": "bytes" + } + ], + "name": "sendL2Message", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendUnsignedTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IMessageProvider.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IMessageProvider.json new file mode 100644 index 0000000..04cf0ab --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/IMessageProvider.json @@ -0,0 +1,34 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "InboxMessageDelivered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + } + ], + "name": "InboxMessageDeliveredFromOrigin", + "type": "event" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperBase.json new file mode 100644 index 0000000..b544978 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperBase.json @@ -0,0 +1,7 @@ +[ + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatible.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatible.json new file mode 100644 index 0000000..aae2720 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatible.json @@ -0,0 +1,44 @@ +[ + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatibleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatibleInterface.json new file mode 100644 index 0000000..e934ed8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatibleInterface.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatibleTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatibleTestHelper.json new file mode 100644 index 0000000..f85d63c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperCompatibleTestHelper.json @@ -0,0 +1,51 @@ +[ + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "testCannotExecute", + "outputs": [], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrar.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrar.json new file mode 100644 index 0000000..2159111 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrar.json @@ -0,0 +1,594 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "LINKAddress", + "type": "address" + }, + { + "internalType": "enum KeeperRegistrar.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "autoApproveMaxAllowed", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AmountMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "FunctionNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "HashMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPayment", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAdminAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "LinkTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyAdminOrOwner", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyLink", + "type": "error" + }, + { + "inputs": [], + "name": "RegistrationRequestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "RequestNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "SenderMismatch", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "AutoApproveAllowedSenderSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "enum KeeperRegistrar.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "autoApproveMaxAllowed", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "displayName", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + } + ], + "name": "RegistrationApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "RegistrationRejected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "source", + "type": "uint8" + } + ], + "name": "RegistrationRequested", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "cancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "senderAddress", + "type": "address" + } + ], + "name": "getAutoApproveAllowedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "getPendingRequest", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrationConfig", + "outputs": [ + { + "internalType": "enum KeeperRegistrar.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "autoApproveMaxAllowed", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "approvedCount", + "type": "uint32" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minLINKJuels", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "internalType": "uint8", + "name": "source", + "type": "uint8" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "senderAddress", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAutoApproveAllowedSender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum KeeperRegistrar.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "autoApproveMaxAllowed", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "name": "setRegistrationConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrar2_0.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrar2_0.json new file mode 100644 index 0000000..fcbcf55 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrar2_0.json @@ -0,0 +1,632 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "LINKAddress", + "type": "address" + }, + { + "internalType": "enum KeeperRegistrar2_0.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "autoApproveMaxAllowed", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AmountMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "FunctionNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "HashMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPayment", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAdminAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "LinkTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyAdminOrOwner", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyLink", + "type": "error" + }, + { + "inputs": [], + "name": "RegistrationRequestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "RequestNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "SenderMismatch", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "AutoApproveAllowedSenderSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "enum KeeperRegistrar2_0.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "autoApproveMaxAllowed", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "displayName", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + } + ], + "name": "RegistrationApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "RegistrationRejected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "RegistrationRequested", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "cancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "senderAddress", + "type": "address" + } + ], + "name": "getAutoApproveAllowedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "getPendingRequest", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrationConfig", + "outputs": [ + { + "internalType": "enum KeeperRegistrar2_0.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "autoApproveMaxAllowed", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "approvedCount", + "type": "uint32" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minLINKJuels", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "senderAddress", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAutoApproveAllowedSender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum KeeperRegistrar2_0.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "autoApproveMaxAllowed", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "name": "setRegistrationConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrarDev.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrarDev.json new file mode 100644 index 0000000..b0da4eb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistrarDev.json @@ -0,0 +1,589 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "LINKAddress", + "type": "address" + }, + { + "internalType": "enum KeeperRegistrarDev.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "autoApproveMaxAllowed", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AmountMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "FunctionNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "HashMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPayment", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAdminAddress", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "LinkTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyAdminOrOwner", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyLink", + "type": "error" + }, + { + "inputs": [], + "name": "RegistrationRequestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "RequestNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "SenderMismatch", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "AutoApproveAllowedSenderSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "enum KeeperRegistrarDev.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "autoApproveMaxAllowed", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "displayName", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + } + ], + "name": "RegistrationApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "RegistrationRejected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "indexed": true, + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "source", + "type": "uint8" + } + ], + "name": "RegistrationRequested", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "cancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "senderAddress", + "type": "address" + } + ], + "name": "getAutoApproveAllowedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + } + ], + "name": "getPendingRequest", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegistrationConfig", + "outputs": [ + { + "internalType": "enum KeeperRegistrarDev.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "autoApproveMaxAllowed", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "approvedCount", + "type": "uint32" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minLINKJuels", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "bytes", + "name": "encryptedEmail", + "type": "bytes" + }, + { + "internalType": "address", + "name": "upkeepContract", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "adminAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + }, + { + "internalType": "uint8", + "name": "source", + "type": "uint8" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "senderAddress", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAutoApproveAllowedSender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum KeeperRegistrarDev.AutoApproveType", + "name": "autoApproveConfigType", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "autoApproveMaxAllowed", + "type": "uint16" + }, + { + "internalType": "address", + "name": "keeperRegistry", + "type": "address" + }, + { + "internalType": "uint96", + "name": "minLINKJuels", + "type": "uint96" + } + ], + "name": "setRegistrationConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry.json new file mode 100644 index 0000000..9cc5753 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry.json @@ -0,0 +1,1486 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + } + ], + "name": "getPeerRegistryMigrationPermission", + "outputs": [ + { + "internalType": "enum KeeperRegistry.MigrationPermission", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "state", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistry.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry1_2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry1_2.json new file mode 100644 index 0000000..0166311 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry1_2.json @@ -0,0 +1,1486 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + } + ], + "name": "getPeerRegistryMigrationPermission", + "outputs": [ + { + "internalType": "enum KeeperRegistry1_2.MigrationPermission", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "state", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistry1_2.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry1_3.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry1_3.json new file mode 100644 index 0000000..24d08ef --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry1_3.json @@ -0,0 +1,1744 @@ +[ + { + "inputs": [ + { + "internalType": "contract KeeperRegistryLogic1_3", + "name": "keeperRegistryLogic", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyAddress", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyUnpausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepCancelled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "UpkeepCheckDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepUnpaused", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "ARB_NITRO_ORACLE", + "outputs": [ + { + "internalType": "contract ArbGasInfo", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "KEEPER_REGISTRY_LOGIC", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_ORACLE", + "outputs": [ + { + "internalType": "contract OVM_GasPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_MODEL", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase.PaymentModel", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REGISTRY_GAS_OVERHEAD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + } + ], + "name": "getPeerRegistryMigrationPermission", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase.MigrationPermission", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "state", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "pauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistryBase.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "unpauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "updateCheckData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry2_0.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry2_0.json new file mode 100644 index 0000000..d52a095 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistry2_0.json @@ -0,0 +1,1790 @@ +[ + { + "inputs": [ + { + "internalType": "enum KeeperRegistryBase2_0.PaymentModel", + "name": "paymentModel", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "registryGasOverhead", + "type": "uint256" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "keeperRegistryLogic", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyUnpausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepCancelled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "UpkeepCheckDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepUnpaused", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "ARB_NITRO_ORACLE", + "outputs": [ + { + "internalType": "contract ArbGasInfo", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "KEEPER_REGISTRY_LOGIC", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L1_FEE_DATA_PADDING", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_INPUT_DATA", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_ORACLE", + "outputs": [ + { + "internalType": "contract OVM_GasPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_MODEL", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase2_0.PaymentModel", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REGISTRY_GAS_OVERHEAD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + } + ], + "name": "getPeerRegistryMigrationPermission", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase2_0.MigrationPermission", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "state", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "pauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistryBase2_0.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "unpauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "updateCheckData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBase.json new file mode 100644 index 0000000..10a30b0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBase.json @@ -0,0 +1,857 @@ +[ + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyAddress", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyUnpausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepCancelled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "UpkeepCheckDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepUnpaused", + "type": "event" + }, + { + "inputs": [], + "name": "ARB_NITRO_ORACLE", + "outputs": [ + { + "internalType": "contract ArbGasInfo", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_ORACLE", + "outputs": [ + { + "internalType": "contract OVM_GasPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_MODEL", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase.PaymentModel", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REGISTRY_GAS_OVERHEAD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBase2_0.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBase2_0.json new file mode 100644 index 0000000..0390ac7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBase2_0.json @@ -0,0 +1,878 @@ +[ + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyUnpausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepCancelled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "UpkeepCheckDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepUnpaused", + "type": "event" + }, + { + "inputs": [], + "name": "ARB_NITRO_ORACLE", + "outputs": [ + { + "internalType": "contract ArbGasInfo", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L1_FEE_DATA_PADDING", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_INPUT_DATA", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_ORACLE", + "outputs": [ + { + "internalType": "contract OVM_GasPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_MODEL", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase2_0.PaymentModel", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REGISTRY_GAS_OVERHEAD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBaseInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBaseInterface.json new file mode 100644 index 0000000..fa6a597 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryBaseInterface.json @@ -0,0 +1,403 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "pauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "unpauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "updateCheckData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryCheckUpkeepGasUsageWrapper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryCheckUpkeepGasUsageWrapper.json new file mode 100644 index 0000000..d7f5c60 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryCheckUpkeepGasUsageWrapper.json @@ -0,0 +1,131 @@ +[ + { + "inputs": [ + { + "internalType": "contract KeeperRegistryExecutableInterface", + "name": "keeperRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperRegistry", + "outputs": [ + { + "internalType": "contract KeeperRegistryExecutableInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "measureCheckGas", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.json new file mode 100644 index 0000000..910cb03 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.json @@ -0,0 +1,131 @@ +[ + { + "inputs": [ + { + "internalType": "contract AutomationRegistryExecutableInterface", + "name": "keeperRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getKeeperRegistry", + "outputs": [ + { + "internalType": "contract AutomationRegistryExecutableInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "measureCheckGas", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryDev.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryDev.json new file mode 100644 index 0000000..fe27819 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryDev.json @@ -0,0 +1,1529 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "CannotChangePayee", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TooFewKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "RegistrarChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "TranscoderChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepImported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "getMaxPaymentForGas", + "outputs": [ + { + "internalType": "uint96", + "name": "maxPayment", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getMinBalanceForUpkeep", + "outputs": [ + { + "internalType": "uint96", + "name": "minBalance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + } + ], + "name": "getPeerRegistryMigrationPermission", + "outputs": [ + { + "internalType": "enum KeeperRegistryDev.MigrationPermission", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "state", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistryDev.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryExecutableInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryExecutableInterface.json new file mode 100644 index 0000000..d90e80e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryExecutableInterface.json @@ -0,0 +1,447 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "pauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "unpauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "updateCheckData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryInterface.json new file mode 100644 index 0000000..f4401b7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryInterface.json @@ -0,0 +1,447 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "upkeepId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "gasWei", + "type": "int256" + }, + { + "internalType": "int256", + "name": "linkEth", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxCount", + "type": "uint256" + } + ], + "name": "getActiveUpkeepIDs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "getKeeperInfo", + "outputs": [ + { + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getState", + "outputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "nonce", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "ownerLinkBalance", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "expectedLinkBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numUpkeeps", + "type": "uint256" + } + ], + "internalType": "struct State", + "name": "", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "internalType": "struct Config", + "name": "", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getUpkeep", + "outputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + }, + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "address", + "name": "lastKeeper", + "type": "address" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "uint64", + "name": "maxValidBlocknumber", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "amountSpent", + "type": "uint96" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "pauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "unpauseUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "updateCheckData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryLogic1_3.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryLogic1_3.json new file mode 100644 index 0000000..fc3f99b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryLogic1_3.json @@ -0,0 +1,1208 @@ +[ + { + "inputs": [ + { + "internalType": "enum KeeperRegistryBase.PaymentModel", + "name": "paymentModel", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "registryGasOverhead", + "type": "uint256" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyAddress", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyUnpausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepCancelled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "UpkeepCheckDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepUnpaused", + "type": "event" + }, + { + "inputs": [], + "name": "ARB_NITRO_ORACLE", + "outputs": [ + { + "internalType": "contract ArbGasInfo", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_ORACLE", + "outputs": [ + { + "internalType": "contract OVM_GasPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_MODEL", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase.PaymentModel", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REGISTRY_GAS_OVERHEAD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistryBase.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryLogic2_0.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryLogic2_0.json new file mode 100644 index 0000000..3fd4fe7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeeperRegistryLogic2_0.json @@ -0,0 +1,1229 @@ +[ + { + "inputs": [ + { + "internalType": "enum KeeperRegistryBase2_0.PaymentModel", + "name": "paymentModel", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "registryGasOverhead", + "type": "uint256" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "fastGasFeed", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ArrayHasNoEntries", + "type": "error" + }, + { + "inputs": [], + "name": "CannotCancel", + "type": "error" + }, + { + "inputs": [], + "name": "DuplicateEntry", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitCanOnlyIncrease", + "type": "error" + }, + { + "inputs": [], + "name": "GasLimitOutsideRange", + "type": "error" + }, + { + "inputs": [], + "name": "IndexOutOfRange", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDataLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPayee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRecipient", + "type": "error" + }, + { + "inputs": [], + "name": "KeepersMustTakeTurns", + "type": "error" + }, + { + "inputs": [], + "name": "MigrationNotPermitted", + "type": "error" + }, + { + "inputs": [], + "name": "NotAContract", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyActiveKeepers", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByLINKToken", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByOwnerOrRegistrar", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedAdmin", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableByProposedPayee", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyPausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "OnlySimulatedBackend", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyUnpausedUpkeep", + "type": "error" + }, + { + "inputs": [], + "name": "ParameterLengthError", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentGreaterThanAllLINK", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "reason", + "type": "bytes" + } + ], + "name": "TargetCheckReverted", + "type": "error" + }, + { + "inputs": [], + "name": "TranscoderNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepCancelled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotCanceled", + "type": "error" + }, + { + "inputs": [], + "name": "UpkeepNotNeeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValueNotChanged", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "paymentPremiumPPB", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "flatFeeMicroLink", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "blockCountPerTurn", + "type": "uint24" + }, + { + "internalType": "uint32", + "name": "checkGasLimit", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "stalenessSeconds", + "type": "uint24" + }, + { + "internalType": "uint16", + "name": "gasCeilingMultiplier", + "type": "uint16" + }, + { + "internalType": "uint96", + "name": "minUpkeepSpend", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "maxPerformGas", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "fallbackGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fallbackLinkPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "transcoder", + "type": "address" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct Config", + "name": "config", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "FundsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "FundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "KeepersUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "OwnerFundsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "PayeeshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "payee", + "type": "address" + } + ], + "name": "PaymentWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "UpkeepAdminTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "atBlockHeight", + "type": "uint64" + } + ], + "name": "UpkeepCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "newCheckData", + "type": "bytes" + } + ], + "name": "UpkeepCheckDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "gasLimit", + "type": "uint96" + } + ], + "name": "UpkeepGasLimitSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "UpkeepMigrated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "performData", + "type": "bytes" + } + ], + "name": "UpkeepPerformed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startingBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "importedFrom", + "type": "address" + } + ], + "name": "UpkeepReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "executeGas", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "UpkeepRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "UpkeepUnpaused", + "type": "event" + }, + { + "inputs": [], + "name": "ARB_NITRO_ORACLE", + "outputs": [ + { + "internalType": "contract ArbGasInfo", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FAST_GAS_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L1_FEE_DATA_PADDING", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_INPUT_DATA", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPTIMISM_ORACLE", + "outputs": [ + { + "internalType": "contract OVM_GasPriceOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAYMENT_MODEL", + "outputs": [ + { + "internalType": "enum KeeperRegistryBase2_0.PaymentModel", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REGISTRY_GAS_OVERHEAD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + } + ], + "name": "acceptPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "acceptUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "addFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "cancelUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bytes", + "name": "performData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "maxLinkPayment", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "adjustedGasWei", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "linkEth", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + }, + { + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "internalType": "bytes", + "name": "checkData", + "type": "bytes" + } + ], + "name": "registerUpkeep", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "keepers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "payees", + "type": "address[]" + } + ], + "name": "setKeepers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "peer", + "type": "address" + }, + { + "internalType": "enum KeeperRegistryBase2_0.MigrationPermission", + "name": "permission", + "type": "uint8" + } + ], + "name": "setPeerRegistryMigrationPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setUpkeepGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "keeper", + "type": "address" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferPayeeship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "transferUpkeepAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawOwnerFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdrawPayment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeepersVRFConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeepersVRFConsumer.json new file mode 100644 index 0000000..aa95d60 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/KeepersVRFConsumer.json @@ -0,0 +1,242 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "uint64", + "name": "subscriptionId", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "upkeepInterval", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [], + "name": "COORDINATOR", + "outputs": [ + { + "internalType": "contract VRFCoordinatorV2Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "KEY_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REQUEST_CONFIRMATIONS", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SUBSCRIPTION_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPKEEP_INTERVAL", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "upkeepNeeded", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_lastTimeStamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_requests", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "fulfilled", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_vrfRequestCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_vrfResponseCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/LinkTokenInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/LinkTokenInterface.json new file mode 100644 index 0000000..dc470f1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/LinkTokenInterface.json @@ -0,0 +1,245 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "remaining", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "decimalPlaces", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "decreaseApproval", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "increaseApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "totalTokensIssued", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "transferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/LogEmitter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/LogEmitter.json new file mode 100644 index 0000000..34f1f6b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/LogEmitter.json @@ -0,0 +1,80 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "Log1", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "Log2", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "", + "type": "string" + } + ], + "name": "Log3", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "v", + "type": "uint256[]" + } + ], + "name": "EmitLog1", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "v", + "type": "uint256[]" + } + ], + "name": "EmitLog2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string[]", + "name": "v", + "type": "string[]" + } + ], + "name": "EmitLog3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MigratableKeeperRegistryInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MigratableKeeperRegistryInterface.json new file mode 100644 index 0000000..87d0f40 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MigratableKeeperRegistryInterface.json @@ -0,0 +1,46 @@ +[ + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "upkeepIDs", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "destination", + "type": "address" + } + ], + "name": "migrateUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "receiveUpkeeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upkeepTranscoderVersion", + "outputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "version", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockAggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockAggregator.json new file mode 100644 index 0000000..7152c4c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockAggregator.json @@ -0,0 +1,41 @@ +[ + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_answer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "name": "setLatestAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockAggregatorValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockAggregatorValidator.json new file mode 100644 index 0000000..130b3bf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockAggregatorValidator.json @@ -0,0 +1,84 @@ +[ + { + "inputs": [ + { + "internalType": "uint8", + "name": "id_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "id", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousRoundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "currentRoundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "ValidateCalled", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "previousRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "currentRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockArbGasInfo.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockArbGasInfo.json new file mode 100644 index 0000000..7b98d24 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockArbGasInfo.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "getCurrentTxL1GasFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockArbitrumInbox.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockArbitrumInbox.json new file mode 100644 index 0000000..c980161 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockArbitrumInbox.json @@ -0,0 +1,462 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "InboxMessageDelivered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "messageNum", + "type": "uint256" + } + ], + "name": "InboxMessageDeliveredFromOrigin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "RetryableTicketNoRefundAliasRewriteCreated", + "type": "event" + }, + { + "inputs": [], + "name": "bridge", + "outputs": [ + { + "internalType": "contract IBridge", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "dataLength", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "baseFee", + "type": "uint256" + } + ], + "name": "calculateRetryableSubmissionFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createRetryableTicket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "arbTxCallValue", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "address", + "name": "submissionRefundAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "valueRefundAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "createRetryableTicketNoRefundAliasRewrite", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + } + ], + "name": "depositEth", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxGasPrice", + "type": "uint256" + } + ], + "name": "depositEthRetryable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendContractTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendL1FundedContractTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendL1FundedUnsignedTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "messageData", + "type": "bytes" + } + ], + "name": "sendL2Message", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPriceBid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "address", + "name": "destAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "sendUnsignedTransaction", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockCompoundOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockCompoundOracle.json new file mode 100644 index 0000000..0ac15a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockCompoundOracle.json @@ -0,0 +1,44 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "price", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "newPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newDecimals", + "type": "uint256" + } + ], + "name": "setPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOVMCrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOVMCrossDomainMessenger.json new file mode 100644 index 0000000..8e63ada --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOVMCrossDomainMessenger.json @@ -0,0 +1,101 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "_setMockMessageSender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOVMGasPriceOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOVMGasPriceOracle.json new file mode 100644 index 0000000..1806919 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOVMGasPriceOracle.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "getL1Fee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOptimismL1CrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOptimismL1CrossDomainMessenger.json new file mode 100644 index 0000000..1a4c380 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOptimismL1CrossDomainMessenger.json @@ -0,0 +1,238 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "messageNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_messageNonce", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "stateRoot", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "batchIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "batchRoot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "batchSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "prevTotalElements", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "extraData", + "type": "bytes" + } + ], + "internalType": "struct Lib_OVMCodec.ChainBatchHeader", + "name": "stateRootBatchHeader", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "siblings", + "type": "bytes32[]" + } + ], + "internalType": "struct Lib_OVMCodec.ChainInclusionProof", + "name": "stateRootProof", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "stateTrieWitness", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "storageTrieWitness", + "type": "bytes" + } + ], + "internalType": "struct IL1CrossDomainMessenger.L2MessageInclusionProof", + "name": "_proof", + "type": "tuple" + } + ], + "name": "relayMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_queueIndex", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "_oldGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_newGasLimit", + "type": "uint32" + } + ], + "name": "replayMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOptimismL2CrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOptimismL2CrossDomainMessenger.json new file mode 100644 index 0000000..eaf980a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockOptimismL2CrossDomainMessenger.json @@ -0,0 +1,146 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "messageNonce", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_messageNonce", + "type": "uint256" + } + ], + "name": "relayMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newSender", + "type": "address" + } + ], + "name": "setSender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockV3Aggregator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockV3Aggregator.json new file mode 100644 index 0000000..bd452b6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MockV3Aggregator.json @@ -0,0 +1,297 @@ +[ + { + "inputs": [ + { + "internalType": "uint8", + "name": "_decimals", + "type": "uint8" + }, + { + "internalType": "int256", + "name": "_initialAnswer", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + } + ], + "name": "updateAnswer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "_answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "_timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startedAt", + "type": "uint256" + } + ], + "name": "updateRoundData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MultiSend.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MultiSend.json new file mode 100644 index 0000000..89d3ba5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/MultiSend.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "transactions", + "type": "bytes" + } + ], + "name": "multiSend", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OCR2Abstract.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OCR2Abstract.json new file mode 100644 index 0000000..1e03d88 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OCR2Abstract.json @@ -0,0 +1,212 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "previousConfigBlockNumber", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "configCount", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "signers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "transmitters", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "f", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "onchainConfig", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "offchainConfigVersion", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "offchainConfig", + "type": "bytes" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + } + ], + "name": "Transmitted", + "type": "event" + }, + { + "inputs": [], + "name": "latestConfigDetails", + "outputs": [ + { + "internalType": "uint32", + "name": "configCount", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "blockNumber", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestConfigDigestAndEpoch", + "outputs": [ + { + "internalType": "bool", + "name": "scanLogs", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "signers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "transmitters", + "type": "address[]" + }, + { + "internalType": "uint8", + "name": "f", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "onchainConfig", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "offchainConfigVersion", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "offchainConfig", + "type": "bytes" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[3]", + "name": "reportContext", + "type": "bytes32[3]" + }, + { + "internalType": "bytes", + "name": "report", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "rs", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "ss", + "type": "bytes32[]" + }, + { + "internalType": "bytes32", + "name": "rawVs", + "type": "bytes32" + } + ], + "name": "transmit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OCR2Base.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OCR2Base.json new file mode 100644 index 0000000..a47ad11 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OCR2Base.json @@ -0,0 +1,296 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "previousConfigBlockNumber", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "configCount", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "signers", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "transmitters", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "f", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "onchainConfig", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "offchainConfigVersion", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "offchainConfig", + "type": "bytes" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + } + ], + "name": "Transmitted", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "latestConfigDetails", + "outputs": [ + { + "internalType": "uint32", + "name": "configCount", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "blockNumber", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestConfigDigestAndEpoch", + "outputs": [ + { + "internalType": "bool", + "name": "scanLogs", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "configDigest", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "epoch", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_signers", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_transmitters", + "type": "address[]" + }, + { + "internalType": "uint8", + "name": "_f", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "_onchainConfig", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "_offchainConfigVersion", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "_offchainConfig", + "type": "bytes" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[3]", + "name": "reportContext", + "type": "bytes32[3]" + }, + { + "internalType": "bytes", + "name": "report", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "rs", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "ss", + "type": "bytes32[]" + }, + { + "internalType": "bytes32", + "name": "rawVs", + "type": "bytes32" + } + ], + "name": "transmit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "transmitters", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OVM_GasPriceOracle.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OVM_GasPriceOracle.json new file mode 100644 index 0000000..735c1b6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OVM_GasPriceOracle.json @@ -0,0 +1,298 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "DecimalsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "GasPriceUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "L1BaseFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "OverheadUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "ScalarUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "gasPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "getL1Fee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "getL1GasUsed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1BaseFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "overhead", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "scalar", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_decimals", + "type": "uint256" + } + ], + "name": "setDecimals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_gasPrice", + "type": "uint256" + } + ], + "name": "setGasPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_baseFee", + "type": "uint256" + } + ], + "name": "setL1BaseFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_overhead", + "type": "uint256" + } + ], + "name": "setOverhead", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_scalar", + "type": "uint256" + } + ], + "name": "setScalar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OperatorInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OperatorInterface.json new file mode 100644 index 0000000..1d0c84b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OperatorInterface.json @@ -0,0 +1,345 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "cancelOracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable[]", + "name": "receivers", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "distributeFunds", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "fulfillOracleRequest2", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAuthorizedSenders", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getForwarder", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "node", + "type": "address" + } + ], + "name": "isAuthorizedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "specId", + "type": "bytes32" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "operatorRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestPrice", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "serviceAgreementID", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dataVersion", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "oracleRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ownerTransferAndCall", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "senders", + "type": "address[]" + } + ], + "name": "setAuthorizedSenders", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismCrossDomainForwarder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismCrossDomainForwarder.json new file mode 100644 index 0000000..ca8ded3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismCrossDomainForwarder.json @@ -0,0 +1,204 @@ +[ + { + "inputs": [ + { + "internalType": "contract iOVM_CrossDomainMessenger", + "name": "crossDomainMessengerAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "l1OwnerAddr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "crossDomainMessenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismCrossDomainGovernor.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismCrossDomainGovernor.json new file mode 100644 index 0000000..5840680 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismCrossDomainGovernor.json @@ -0,0 +1,222 @@ +[ + { + "inputs": [ + { + "internalType": "contract iOVM_CrossDomainMessenger", + "name": "crossDomainMessengerAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "l1OwnerAddr", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "crossDomainMessenger", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "forwardDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "l1Owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Ownership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismSequencerUptimeFeed.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismSequencerUptimeFeed.json new file mode 100644 index 0000000..3cd4473 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismSequencerUptimeFeed.json @@ -0,0 +1,583 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "l1SenderAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "l2CrossDomainMessengerAddr", + "type": "address" + }, + { + "internalType": "bool", + "name": "initialStatus", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidSender", + "type": "error" + }, + { + "inputs": [], + "name": "NoDataPresent", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "int256", + "name": "current", + "type": "int256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + } + ], + "name": "AnswerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "L1SenderTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "startedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + } + ], + "name": "NewRound", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "int256", + "name": "status", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "updatedAt", + "type": "uint64" + } + ], + "name": "RoundUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "latestStatus", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "latestTimestamp", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "bool", + "name": "incomingStatus", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "incomingTimestamp", + "type": "uint64" + } + ], + "name": "UpdateIgnored", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "roundId", + "type": "uint256" + } + ], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1Sender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRound", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferL1Sender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "status", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "name": "updateStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismSequencerUptimeFeedInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismSequencerUptimeFeedInterface.json new file mode 100644 index 0000000..a67bc9d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismSequencerUptimeFeedInterface.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "bool", + "name": "status", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + } + ], + "name": "updateStatus", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismValidator.json new file mode 100644 index 0000000..f6a8f89 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OptimismValidator.json @@ -0,0 +1,321 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "l1CrossDomainMessengerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "l2UptimeFeedAddr", + "type": "address" + }, + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "GasLimitUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "L1_CROSS_DOMAIN_MESSENGER_ADDRESS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "L2_UPTIME_FEED_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getGasLimit", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "gasLimit", + "type": "uint32" + } + ], + "name": "setGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OracleInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OracleInterface.json new file mode 100644 index 0000000..c69d26a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OracleInterface.json @@ -0,0 +1,96 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "payment", + "type": "uint256" + }, + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "callbackFunctionId", + "type": "bytes4" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "fulfillOracleRequest", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "node", + "type": "address" + } + ], + "name": "isAuthorizedSender", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OwnableInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OwnableInterface.json new file mode 100644 index 0000000..ee9d817 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/OwnableInterface.json @@ -0,0 +1,35 @@ +[ + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PermissionedForwardProxy.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PermissionedForwardProxy.json new file mode 100644 index 0000000..2e61e1e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PermissionedForwardProxy.json @@ -0,0 +1,183 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "PermissionNotSet", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "PermissionRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "PermissionSet", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "handler", + "type": "bytes" + } + ], + "name": "forward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "getPermission", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "removePermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "setPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PoRAddressList.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PoRAddressList.json new file mode 100644 index 0000000..5f3bb0b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PoRAddressList.json @@ -0,0 +1,39 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endIndex", + "type": "uint256" + } + ], + "name": "getPoRAddressList", + "outputs": [ + { + "internalType": "string[]", + "name": "", + "type": "string[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPoRAddressListLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PointerInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PointerInterface.json new file mode 100644 index 0000000..c5ae2c8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/PointerInterface.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "getAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveEmitter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveEmitter.json new file mode 100644 index 0000000..6a75bdb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveEmitter.json @@ -0,0 +1,25 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "name": "FundsReceived", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveFallbackEmitter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveFallbackEmitter.json new file mode 100644 index 0000000..7c7fdcf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveFallbackEmitter.json @@ -0,0 +1,25 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "name": "FundsReceived", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveReverter.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveReverter.json new file mode 100644 index 0000000..d48d964 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ReceiveReverter.json @@ -0,0 +1,6 @@ +[ + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/SimpleReadAccessController.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/SimpleReadAccessController.json new file mode 100644 index 0000000..e7a38bf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/SimpleReadAccessController.json @@ -0,0 +1,188 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_calldata", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/SimpleWriteAccessController.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/SimpleWriteAccessController.json new file mode 100644 index 0000000..ecc2ca0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/SimpleWriteAccessController.json @@ -0,0 +1,193 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "AddedAccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "CheckAccessEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RemovedAccess", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "addAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enableAccessCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "hasAccess", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "removeAccess", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/StalenessFlaggingValidator.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/StalenessFlaggingValidator.json new file mode 100644 index 0000000..d90a73f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/StalenessFlaggingValidator.json @@ -0,0 +1,266 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "previous", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "current", + "type": "uint256" + } + ], + "name": "FlaggingThresholdUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "FlagsAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "checkUpkeep", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flags", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "performUpkeep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "flagsAddress", + "type": "address" + } + ], + "name": "setFlagsAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "flaggingThresholds", + "type": "uint256[]" + } + ], + "name": "setThresholds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "threshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "aggregators", + "type": "address[]" + } + ], + "name": "update", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/TypeAndVersionInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/TypeAndVersionInterface.json new file mode 100644 index 0000000..99725a5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/TypeAndVersionInterface.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UniswapAnchoredView.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UniswapAnchoredView.json new file mode 100644 index 0000000..0f5eff3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UniswapAnchoredView.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "price", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UpkeepTranscoder.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UpkeepTranscoder.json new file mode 100644 index 0000000..977aa03 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UpkeepTranscoder.json @@ -0,0 +1,49 @@ +[ + { + "inputs": [], + "name": "InvalidTranscoding", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "fromVersion", + "type": "uint8" + }, + { + "internalType": "enum UpkeepFormat", + "name": "toVersion", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "transcodeUpkeeps", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UpkeepTranscoderInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UpkeepTranscoderInterface.json new file mode 100644 index 0000000..83cadad --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/UpkeepTranscoderInterface.json @@ -0,0 +1,31 @@ +[ + { + "inputs": [ + { + "internalType": "enum UpkeepFormat", + "name": "fromVersion", + "type": "uint8" + }, + { + "internalType": "enum UpkeepFormat", + "name": "toVersion", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "encodedUpkeeps", + "type": "bytes" + } + ], + "name": "transcodeUpkeeps", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRF.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRF.json new file mode 100644 index 0000000..d1bdb6d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRF.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "PROOF_LENGTH", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumer.json new file mode 100644 index 0000000..2cdbd04 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumer.json @@ -0,0 +1,86 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "randomnessOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "requestId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "testRequestRandomness", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerBase.json new file mode 100644 index 0000000..2f5c323 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerBase.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerBaseV2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerBaseV2.json new file mode 100644 index 0000000..413f472 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerBaseV2.json @@ -0,0 +1,36 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerExternalSubOwnerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerExternalSubOwnerExample.json new file mode 100644 index 0000000..de4a287 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerExternalSubOwnerExample.json @@ -0,0 +1,92 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "requestRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "setSubscriptionID", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerV2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerV2.json new file mode 100644 index 0000000..dd2e22e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFConsumerV2.json @@ -0,0 +1,188 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_gasAvailable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_randomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_subId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "testCreateSubscriptionAndFund", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "minReqConfs", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + } + ], + "name": "testRequestRandomness", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "topUpSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "name": "updateSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorMock.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorMock.json new file mode 100644 index 0000000..d386885 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorMock.json @@ -0,0 +1,97 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "linkAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "seed", + "type": "uint256" + } + ], + "name": "RandomnessRequest", + "type": "event" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + }, + { + "internalType": "address", + "name": "consumerContract", + "type": "address" + } + ], + "name": "callBackWithRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2.json new file mode 100644 index 0000000..5560b08 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2.json @@ -0,0 +1,1463 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "blockhashStore", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "internalBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "externalBalance", + "type": "uint256" + } + ], + "name": "BalanceInvariantViolated", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "blockNum", + "type": "uint256" + } + ], + "name": "BlockhashNotInStore", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "have", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "want", + "type": "uint32" + } + ], + "name": "GasLimitTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectCommitment", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "have", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "want", + "type": "uint256" + } + ], + "name": "InsufficientGasForConsumer", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCalldata", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "InvalidConsumer", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "linkWei", + "type": "int256" + } + ], + "name": "InvalidLinkWeiPrice", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "have", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "min", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "max", + "type": "uint16" + } + ], + "name": "InvalidRequestConfirmations", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSubscription", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposedOwner", + "type": "address" + } + ], + "name": "MustBeRequestedOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "MustBeSubOwner", + "type": "error" + }, + { + "inputs": [], + "name": "NoCorrespondingRequest", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "NoSuchProvingKey", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "have", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "want", + "type": "uint32" + } + ], + "name": "NumWordsTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableFromLink", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "PendingRequestExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "ProvingKeyAlreadyRegistered", + "type": "error" + }, + { + "inputs": [], + "name": "Reentrant", + "type": "error" + }, + { + "inputs": [], + "name": "TooManyConsumers", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "int256", + "name": "fallbackWeiPerUnitLink", + "type": "int256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "indexed": false, + "internalType": "struct VRFCoordinatorV2.FeeConfig", + "name": "feeConfig", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FundsRecovered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + } + ], + "name": "ProvingKeyDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + } + ], + "name": "ProvingKeyRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "outputSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "name": "RandomWordsFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "preSeed", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RandomWordsRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SubscriptionCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "SubscriptionConsumerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "SubscriptionConsumerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "SubscriptionCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "name": "SubscriptionFunded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "SubscriptionOwnerTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "SubscriptionOwnerTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "BLOCKHASH_STORE", + "outputs": [ + { + "internalType": "contract BlockhashStoreInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_CONSUMERS", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_NUM_WORDS", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_REQUEST_CONFIRMATIONS", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "acceptSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "addConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "cancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "createSubscription", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "publicProvingKey", + "type": "uint256[2]" + } + ], + "name": "deregisterProvingKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "cGammaWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sHashWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "internalType": "struct VRF.Proof", + "name": "proof", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "blockNum", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "internalType": "struct VRFCoordinatorV2.RequestCommitment", + "name": "rc", + "type": "tuple" + } + ], + "name": "fulfillRandomWords", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "name": "getCommitment", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentSubId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFallbackWeiPerUnitLink", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + } + ], + "name": "getFeeTier", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRequestConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "getSubscription", + "outputs": [ + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "publicKey", + "type": "uint256[2]" + } + ], + "name": "hashOfKey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "oracleWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "ownerCancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "pendingRequestExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "publicProvingKey", + "type": "uint256[2]" + } + ], + "name": "registerProvingKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "removeConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + } + ], + "name": "requestRandomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "requestSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + }, + { + "internalType": "int256", + "name": "fallbackWeiPerUnitLink", + "type": "int256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "internalType": "struct VRFCoordinatorV2.FeeConfig", + "name": "feeConfig", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2Interface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2Interface.json new file mode 100644 index 0000000..1b16915 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2Interface.json @@ -0,0 +1,215 @@ +[ + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "acceptSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "addConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "cancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "createSubscription", + "outputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getRequestConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "getSubscription", + "outputs": [ + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "pendingRequestExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "removeConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + } + ], + "name": "requestRandomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "requestSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2Mock.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2Mock.json new file mode 100644 index 0000000..119c50a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2Mock.json @@ -0,0 +1,676 @@ +[ + { + "inputs": [ + { + "internalType": "uint96", + "name": "_baseFee", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "_gasPriceLink", + "type": "uint96" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidConsumer", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRandomWords", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSubscription", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "MustBeSubOwner", + "type": "error" + }, + { + "inputs": [], + "name": "TooManyConsumers", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "ConsumerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "ConsumerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "outputSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "name": "RandomWordsFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "preSeed", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RandomWordsRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SubscriptionCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "SubscriptionCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "name": "SubscriptionFunded", + "type": "event" + }, + { + "inputs": [], + "name": "BASE_FEE", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GAS_PRICE_LINK", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_CONSUMERS", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + } + ], + "name": "acceptSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_consumer", + "type": "address" + } + ], + "name": "addConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "cancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_consumer", + "type": "address" + } + ], + "name": "consumerIsAdded", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "createSubscription", + "outputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_consumer", + "type": "address" + } + ], + "name": "fulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_consumer", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_words", + "type": "uint256[]" + } + ], + "name": "fulfillRandomWordsWithOverride", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "uint96", + "name": "_amount", + "type": "uint96" + } + ], + "name": "fundSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFallbackWeiPerUnitLink", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRequestConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + } + ], + "name": "getSubscription", + "outputs": [ + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "pendingRequestExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_consumer", + "type": "address" + } + ], + "name": "removeConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "_minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_numWords", + "type": "uint32" + } + ], + "name": "requestRandomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_newOwner", + "type": "address" + } + ], + "name": "requestSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2TestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2TestHelper.json new file mode 100644 index 0000000..6e1a87c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFCoordinatorV2TestHelper.json @@ -0,0 +1,1512 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "address", + "name": "blockhashStore", + "type": "address" + }, + { + "internalType": "address", + "name": "linkEthFeed", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "internalBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "externalBalance", + "type": "uint256" + } + ], + "name": "BalanceInvariantViolated", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "blockNum", + "type": "uint256" + } + ], + "name": "BlockhashNotInStore", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "have", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "want", + "type": "uint32" + } + ], + "name": "GasLimitTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectCommitment", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "have", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "want", + "type": "uint256" + } + ], + "name": "InsufficientGasForConsumer", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCalldata", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "InvalidConsumer", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "linkWei", + "type": "int256" + } + ], + "name": "InvalidLinkWeiPrice", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "have", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "min", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "max", + "type": "uint16" + } + ], + "name": "InvalidRequestConfirmations", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSubscription", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposedOwner", + "type": "address" + } + ], + "name": "MustBeRequestedOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "MustBeSubOwner", + "type": "error" + }, + { + "inputs": [], + "name": "NoCorrespondingRequest", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "NoSuchProvingKey", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "have", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "want", + "type": "uint32" + } + ], + "name": "NumWordsTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyCallableFromLink", + "type": "error" + }, + { + "inputs": [], + "name": "PaymentTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "PendingRequestExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "ProvingKeyAlreadyRegistered", + "type": "error" + }, + { + "inputs": [], + "name": "Reentrant", + "type": "error" + }, + { + "inputs": [], + "name": "TooManyConsumers", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "int256", + "name": "fallbackWeiPerUnitLink", + "type": "int256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "indexed": false, + "internalType": "struct VRFCoordinatorV2.FeeConfig", + "name": "feeConfig", + "type": "tuple" + } + ], + "name": "ConfigSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FundsRecovered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + } + ], + "name": "ProvingKeyDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + } + ], + "name": "ProvingKeyRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "outputSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "payment", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "name": "RandomWordsFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "preSeed", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RandomWordsRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SubscriptionCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "SubscriptionConsumerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "SubscriptionConsumerRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "SubscriptionCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "name": "SubscriptionFunded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "SubscriptionOwnerTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "SubscriptionOwnerTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "BLOCKHASH_STORE", + "outputs": [ + { + "internalType": "contract BlockhashStoreInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_CONSUMERS", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_NUM_WORDS", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_REQUEST_CONFIRMATIONS", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "acceptSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "addConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasAfterPaymentCalculation", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPM", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "weiPerUnitGas", + "type": "uint256" + } + ], + "name": "calculatePaymentAmountTest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "cancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "createSubscription", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "publicProvingKey", + "type": "uint256[2]" + } + ], + "name": "deregisterProvingKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "cGammaWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sHashWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "internalType": "struct VRF.Proof", + "name": "proof", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "blockNum", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "internalType": "struct VRFCoordinatorV2.RequestCommitment", + "name": "rc", + "type": "tuple" + } + ], + "name": "fulfillRandomWords", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "name": "getCommitment", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentSubId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFallbackWeiPerUnitLink", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeConfig", + "outputs": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + } + ], + "name": "getFeeTier", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGasStart", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPaymentAmount", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRequestConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "getSubscription", + "outputs": [ + { + "internalType": "uint96", + "name": "balance", + "type": "uint96" + }, + { + "internalType": "uint64", + "name": "reqCount", + "type": "uint64" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "publicKey", + "type": "uint256[2]" + } + ], + "name": "hashOfKey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "oracleWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "ownerCancelSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + } + ], + "name": "pendingRequestExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "publicProvingKey", + "type": "uint256[2]" + } + ], + "name": "registerProvingKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "removeConsumer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + } + ], + "name": "requestRandomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "requestSubscriptionOwnerTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "minimumRequestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "maxGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "gasAfterPaymentCalculation", + "type": "uint32" + }, + { + "internalType": "int256", + "name": "fallbackWeiPerUnitLink", + "type": "int256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier1", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier2", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier3", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier4", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPMTier5", + "type": "uint32" + }, + { + "internalType": "uint24", + "name": "reqsForTier2", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier3", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier4", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "reqsForTier5", + "type": "uint24" + } + ], + "internalType": "struct VRFCoordinatorV2.FeeConfig", + "name": "feeConfig", + "type": "tuple" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFExternalSubOwnerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFExternalSubOwnerExample.json new file mode 100644 index 0000000..eb693d3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFExternalSubOwnerExample.json @@ -0,0 +1,130 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "requestRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_randomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFLoadTestExternalSubOwner.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFLoadTestExternalSubOwner.json new file mode 100644 index 0000000..82084df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFLoadTestExternalSubOwner.json @@ -0,0 +1,190 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "_link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "COORDINATOR", + "outputs": [ + { + "internalType": "contract VRFCoordinatorV2Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "_requestConfirmations", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "_requestCount", + "type": "uint16" + } + ], + "name": "requestRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_responseCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFLoadTestOwnerlessConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFLoadTestOwnerlessConsumer.json new file mode 100644 index 0000000..babe0d6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFLoadTestOwnerlessConsumer.json @@ -0,0 +1,90 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_price", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "PRICE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_responseCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFMaliciousConsumerV2.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFMaliciousConsumerV2.json new file mode 100644 index 0000000..be07933 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFMaliciousConsumerV2.json @@ -0,0 +1,162 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_gasAvailable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_randomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_subId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "name": "setKeyHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "testCreateSubscriptionAndFund", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "testRequestRandomness", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "name": "updateSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFOwnerlessConsumerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFOwnerlessConsumerExample.json new file mode 100644 index 0000000..9254332 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFOwnerlessConsumerExample.json @@ -0,0 +1,90 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "_link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "OnlyCallableFromLink", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "requestId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "randomness", + "type": "uint256" + } + ], + "name": "rawFulfillRandomness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_randomnessOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFRequestIDBaseTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFRequestIDBaseTestHelper.json new file mode 100644 index 0000000..53bb38b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFRequestIDBaseTestHelper.json @@ -0,0 +1,60 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_vRFInputSeed", + "type": "uint256" + } + ], + "name": "makeRequestId_", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_userSeed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_requester", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + } + ], + "name": "makeVRFInputSeed_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFSingleConsumerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFSingleConsumerExample.json new file mode 100644 index 0000000..5a4eb85 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFSingleConsumerExample.json @@ -0,0 +1,208 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "fundAndRequestRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "requestRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_randomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestConfig", + "outputs": [ + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "subscribe", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "topUpSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "unsubscribe", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFTestHelper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFTestHelper.json new file mode 100644 index 0000000..0c2fe93 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFTestHelper.json @@ -0,0 +1,474 @@ +[ + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "p1", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "p2", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "invZ", + "type": "uint256" + } + ], + "name": "affineECAdd_", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "base", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exponent", + "type": "uint256" + } + ], + "name": "bigModExp_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "x", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "scalar", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "q", + "type": "uint256[2]" + } + ], + "name": "ecmulVerify_", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "b", + "type": "bytes" + } + ], + "name": "fieldHash_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "name": "hashToCurve_", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "p", + "type": "uint256[2]" + } + ], + "name": "isOnCurve_", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "p1", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "cp1Witness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "p2", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sp2Witness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "name": "linearCombination_", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "px", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "py", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "qx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "qy", + "type": "uint256" + } + ], + "name": "projectiveECAdd_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "cGammaWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sHashWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "internalType": "struct VRF.Proof", + "name": "proof", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + } + ], + "name": "randomValueFromVRFProof_", + "outputs": [ + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "hash", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "v", + "type": "uint256[2]" + } + ], + "name": "scalarFromCurvePoints_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "name": "squareRoot_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256[2]", + "name": "p", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "address", + "name": "lcWitness", + "type": "address" + } + ], + "name": "verifyLinearCombinationWithGenerator_", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "pk", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "gamma", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "c", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "s", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seed", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uWitness", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "cGammaWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "sHashWitness", + "type": "uint256[2]" + }, + { + "internalType": "uint256", + "name": "zInv", + "type": "uint256" + } + ], + "name": "verifyVRFProof_", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "name": "ySquared_", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2RevertingExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2RevertingExample.json new file mode 100644 index 0000000..dd2e22e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2RevertingExample.json @@ -0,0 +1,188 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vrfCoordinator", + "type": "address" + }, + { + "internalType": "address", + "name": "link", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "s_gasAvailable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_randomWords", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_requestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_subId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "testCreateSubscriptionAndFund", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "subId", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "minReqConfs", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "numWords", + "type": "uint32" + } + ], + "name": "testRequestRandomness", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "amount", + "type": "uint96" + } + ], + "name": "topUpSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "consumers", + "type": "address[]" + } + ], + "name": "updateSubscription", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2Wrapper.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2Wrapper.json new file mode 100644 index 0000000..0a19c0f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2Wrapper.json @@ -0,0 +1,469 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_linkEthFeed", + "type": "address" + }, + { + "internalType": "address", + "name": "_coordinator", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "have", + "type": "address" + }, + { + "internalType": "address", + "name": "want", + "type": "address" + } + ], + "name": "OnlyCoordinatorCanFulfill", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "consumer", + "type": "address" + } + ], + "name": "WrapperFulfillmentFailed", + "type": "event" + }, + { + "inputs": [], + "name": "COORDINATOR", + "outputs": [ + { + "internalType": "contract ExtendedVRFCoordinatorV2Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK", + "outputs": [ + { + "internalType": "contract LinkTokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LINK_ETH_FEED", + "outputs": [ + { + "internalType": "contract AggregatorV3Interface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SUBSCRIPTION_ID", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + } + ], + "name": "calculateRequestPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "disable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "enable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "_requestGasPriceWei", + "type": "uint256" + } + ], + "name": "estimateRequestPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getConfig", + "outputs": [ + { + "internalType": "int256", + "name": "fallbackWeiPerUnitLink", + "type": "int256" + }, + { + "internalType": "uint32", + "name": "stalenessSeconds", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fulfillmentFlatFeeLinkPPM", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "wrapperGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "coordinatorGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "wrapperPremiumPercentage", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "keyHash", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "maxNumWords", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastRequestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "onTokenTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_callbacks", + "outputs": [ + { + "internalType": "address", + "name": "callbackAddress", + "type": "address" + }, + { + "internalType": "uint32", + "name": "callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "requestGasPrice", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "requestWeiPerUnitLink", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "juelsPaid", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_configured", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "s_disabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_wrapperGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "_coordinatorGasOverhead", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "_wrapperPremiumPercentage", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_keyHash", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "_maxNumWords", + "type": "uint8" + } + ], + "name": "setConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperConsumerBase.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperConsumerBase.json new file mode 100644 index 0000000..797e6f6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperConsumerBase.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperConsumerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperConsumerExample.json new file mode 100644 index 0000000..0c09d13 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperConsumerExample.json @@ -0,0 +1,233 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_vrfV2Wrapper", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payment", + "type": "uint256" + } + ], + "name": "WrappedRequestFulfilled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid", + "type": "uint256" + } + ], + "name": "WrapperRequestMade", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + } + ], + "name": "getRequestStatus", + "outputs": [ + { + "internalType": "uint256", + "name": "paid", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "fulfilled", + "type": "bool" + }, + { + "internalType": "uint256[]", + "name": "randomWords", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "_requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "_numWords", + "type": "uint32" + } + ], + "name": "makeRequest", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "s_requests", + "outputs": [ + { + "internalType": "uint256", + "name": "paid", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "fulfilled", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperInterface.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperInterface.json new file mode 100644 index 0000000..6618881 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperInterface.json @@ -0,0 +1,58 @@ +[ + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + } + ], + "name": "calculateRequestPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "_requestGasPriceWei", + "type": "uint256" + } + ], + "name": "estimateRequestPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastRequestId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperOutOfGasConsumerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperOutOfGasConsumerExample.json new file mode 100644 index 0000000..b4333ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperOutOfGasConsumerExample.json @@ -0,0 +1,136 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_vrfV2Wrapper", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "_requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "_numWords", + "type": "uint32" + } + ], + "name": "makeRequest", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperRevertingConsumerExample.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperRevertingConsumerExample.json new file mode 100644 index 0000000..b4333ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperRevertingConsumerExample.json @@ -0,0 +1,136 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_vrfV2Wrapper", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "_requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "_numWords", + "type": "uint32" + } + ], + "name": "makeRequest", + "outputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_requestId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_randomWords", + "type": "uint256[]" + } + ], + "name": "rawFulfillRandomWords", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperUnderFundingConsumer.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperUnderFundingConsumer.json new file mode 100644 index 0000000..c2dee64 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/VRFV2WrapperUnderFundingConsumer.json @@ -0,0 +1,112 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_link", + "type": "address" + }, + { + "internalType": "address", + "name": "_vrfV2Wrapper", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "_callbackGasLimit", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "_requestConfirmations", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "_numWords", + "type": "uint32" + } + ], + "name": "makeRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ValidatorProxy.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ValidatorProxy.json new file mode 100644 index 0000000..e570c0e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/ValidatorProxy.json @@ -0,0 +1,323 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "internalType": "contract AggregatorValidatorInterface", + "name": "validator", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "aggregator", + "type": "address" + } + ], + "name": "AggregatorProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "current", + "type": "address" + } + ], + "name": "AggregatorUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposed", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousRoundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "currentRoundId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "ProposedAggregatorValidateCall", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract AggregatorValidatorInterface", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract AggregatorValidatorInterface", + "name": "previous", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract AggregatorValidatorInterface", + "name": "current", + "type": "address" + } + ], + "name": "ValidatorUpgraded", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAggregators", + "outputs": [ + { + "internalType": "address", + "name": "current", + "type": "address" + }, + { + "internalType": "bool", + "name": "hasProposal", + "type": "bool" + }, + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getValidators", + "outputs": [ + { + "internalType": "contract AggregatorValidatorInterface", + "name": "current", + "type": "address" + }, + { + "internalType": "bool", + "name": "hasProposal", + "type": "bool" + }, + { + "internalType": "contract AggregatorValidatorInterface", + "name": "proposed", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposed", + "type": "address" + } + ], + "name": "proposeNewAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract AggregatorValidatorInterface", + "name": "proposed", + "type": "address" + } + ], + "name": "proposeNewValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "typeAndVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "upgradeAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upgradeValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "previousRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "previousAnswer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "currentRoundId", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "currentAnswer", + "type": "int256" + } + ], + "name": "validate", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/abi/v0.8/iOVM_CrossDomainMessenger.json b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/iOVM_CrossDomainMessenger.json new file mode 100644 index 0000000..7d0e9a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/abi/v0.8/iOVM_CrossDomainMessenger.json @@ -0,0 +1,77 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "FailedRelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "msgHash", + "type": "bytes32" + } + ], + "name": "RelayedMessage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "SentMessage", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "_gasLimit", + "type": "uint32" + } + ], + "name": "sendMessage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "xDomainMessageSender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/chainlink-brownie-contracts/contracts/app.config.json b/lib/chainlink-brownie-contracts/contracts/app.config.json new file mode 100644 index 0000000..d853c83 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/app.config.json @@ -0,0 +1,22 @@ +{ + "contractsDir": "src", + "artifactsDir": "abi", + "contractAbstractionDir": ".", + "useDockerisedSolc": false, + "compilerSettings": { + "optimizer": { + "runs": 1000000, + "enabled": true + }, + "versions": { + "v0.4": "0.4.24", + "v0.5": "0.5.0", + "v0.6": "0.6.6", + "v0.7": "0.7.6" + }, + "metadata": { + "bytecodeHash": "none" + } + }, + "publicVersions": ["0.4.24", "0.5.0"] +} diff --git a/lib/chainlink-brownie-contracts/contracts/hardhat.config.ts b/lib/chainlink-brownie-contracts/contracts/hardhat.config.ts new file mode 100644 index 0000000..8dff170 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/hardhat.config.ts @@ -0,0 +1,56 @@ +import "@nomiclabs/hardhat-waffle"; +import "hardhat-contract-sizer"; + +const COMPILER_SETTINGS = { + optimizer: { + enabled: true, + runs: 1000000, + }, + metadata: { + bytecodeHash: "none", + }, +}; + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +export default { + paths: { + artifacts: "./artifacts", + cache: "./cache", + sources: "./src", + tests: "./test", + }, + networks: { + hardhat: {}, + }, + solidity: { + compilers: [ + { + version: "0.4.24", + settings: COMPILER_SETTINGS, + }, + { + version: "0.5.0", + settings: COMPILER_SETTINGS, + }, + { + version: "0.6.6", + settings: COMPILER_SETTINGS, + }, + { + version: "0.7.6", + settings: COMPILER_SETTINGS, + }, + { + version: "0.8.6", + settings: COMPILER_SETTINGS, + }, + ], + }, + contractSizer: { + alphaSort: true, + runOnCompile: false, + disambiguatePaths: false, + }, +}; diff --git a/lib/chainlink-brownie-contracts/contracts/jest.config.js b/lib/chainlink-brownie-contracts/contracts/jest.config.js new file mode 100644 index 0000000..2723261 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/jest.config.js @@ -0,0 +1,104 @@ +/** +* MIT License +* +* Copyright (c) 2021 Patrick +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +/** +* MIT License +* +* Copyright (c) 2021 Patrick +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +/** +* MIT License +* +* Copyright (c) 2021 Patrick Collins +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +/** +* MIT License +* +* Copyright (c) 2021 Patrick Collins +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testRunner: 'jest-circus/runner', + testPathIgnorePatterns: ['/node_modules/', 'dist/'], + globals: { + 'ts-jest': { + tsConfig: 'tsconfig.test.json', + }, + }, + extraGlobals: ['Math'], +} diff --git a/lib/chainlink-brownie-contracts/contracts/package.json b/lib/chainlink-brownie-contracts/contracts/package.json new file mode 100644 index 0000000..a3b581f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/package.json @@ -0,0 +1,57 @@ +{ + "name": "@chainlink/contracts", + "version": "0.5.1", + "description": "Chainlink smart contracts", + "author": "Chainlink devs", + "license": "MIT", + "private": false, + "scripts": { + "test": "hardhat test", + "size": "hardhat size-contracts", + "clean": "hardhat clean", + "compile:native": "./scripts/native_solc_compile_all", + "compile": "hardhat compile", + "coverage": "hardhat coverage", + "setup": "yarn compile", + "prepublishOnly": "yarn compile && ./scripts/prepublish_generate_abi_folder", + "publish-beta": "yarn publish --tag beta", + "publish-prod": "yarn tag add @chainlink/contracts@0.5.1 latest" + }, + "files": [ + "src/", + "abi/" + ], + "devDependencies": { + "@ethereum-waffle/mock-contract": "^3.3.0", + "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-waffle": "^2.0.1", + "@openzeppelin/test-helpers": "^0.5.11", + "@typechain/ethers-v5": "^7.0.1", + "@typechain/hardhat": "^3.0.0", + "@types/cbor": "5.0.1", + "@types/chai": "^4.2.18", + "@types/debug": "^4.1.7", + "@types/mocha": "^8.2.2", + "@types/node": "^15.12.2", + "chai": "^4.3.4", + "debug": "^4.3.2", + "ethereum-waffle": "^3.3.0", + "ethers": "^5.3.1", + "hardhat": "^2.10.1", + "hardhat-abi-exporter": "^2.2.1", + "hardhat-contract-sizer": "^2.5.1", + "moment": "^2.29.4", + "prettier-plugin-solidity": "1.0.0-beta.18", + "solhint": "^3.3.6", + "solhint-plugin-prettier": "^0.0.5", + "solidity-coverage": "^0.7.17", + "ts-node": "^10.0.0", + "typechain": "^5.0.0", + "typescript": "^4.3.2" + }, + "dependencies": { + "@openzeppelin/contracts": "^4.3.3", + "@openzeppelin/contracts-v0.7": "npm:@openzeppelin/contracts@v3.4.2", + "@eth-optimism/contracts": "^0.5.21" + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/scripts/native_solc6_compile b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc6_compile new file mode 100755 index 0000000..ae3be9b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc6_compile @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# This script generates .abi and .bin files for a selected .sol contract. +# Example call: +# ./contracts/scripts/native_solc_compile dev/Operator.sol +# +# The resulting abi and bin files are stored in ./contracts/solc/v0.6 + +SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd .. && pwd -P )" + +solc-select use 0.6.6 +solc --overwrite --optimize --optimize-runs 1000000 --metadata-hash none \ + -o $SCRIPTPATH/solc/v0.6 \ + --abi --bin --allow-paths $SCRIPTPATH/src/v0.6,$SCRIPTPATH/src/v0.6/dev,$SCRIPTPATH/src/v0.6/interfaces,$SCRIPTPATH/src/v0.6/examples,$SCRIPTPATH/src/v0.6/tests,$SCRIPTPATH/src/v0.6/vendor \ + $SCRIPTPATH/src/v0.6/$1 diff --git a/lib/chainlink-brownie-contracts/contracts/scripts/native_solc7_compile b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc7_compile new file mode 100755 index 0000000..dc4c08e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc7_compile @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# This script generates .abi and .bin files for a selected .sol contract. +# Example call: +# ./contracts/scripts/native_solc_compile dev/Operator.sol +# +# The resulting abi and bin files are stored in ./contracts/solc/v0.7 + +SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd .. && pwd -P )" + +solc-select use 0.7.6 +solc --overwrite --optimize --optimize-runs 1000000 --metadata-hash none \ + -o $SCRIPTPATH/solc/v0.7 \ + --abi --bin --allow-paths $SCRIPTPATH/src/v0.7,$SCRIPTPATH/src/v0.7/dev,$SCRIPTPATH/src/v0.7/interfaces,$SCRIPTPATH/src/v0.7/tests,$SCRIPTPATH/src/v0.7/vendor \ + $SCRIPTPATH/src/v0.7/$1 \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/scripts/native_solc8_compile b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc8_compile new file mode 100755 index 0000000..c67e428 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc8_compile @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# This script generates .abi and .bin files for a selected .sol contract. +# Example call: +# ./contracts/scripts/native_solc_compile dev/Operator.sol +# +# The resulting abi and bin files are stored in ./contracts/solc/v0.8 + +SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd .. && pwd -P )" + +solc-select use 0.8.6 +solc --overwrite --optimize --optimize-runs 1000000 --metadata-hash none \ + -o $SCRIPTPATH/solc/v0.8 \ + --abi --bin --allow-paths $SCRIPTPATH/src/v0.8,$SCRIPTPATH/src/v0.8/dev,$SCRIPTPATH/src/v0.8/interfaces,$SCRIPTPATH/src/v0.8/mocks,$SCRIPTPATH/src/v0.8/tests,$SCRIPTPATH/src/v0.8/vendor \ + $SCRIPTPATH/src/v0.8/$1 diff --git a/lib/chainlink-brownie-contracts/contracts/scripts/native_solc_compile_all b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc_compile_all new file mode 100755 index 0000000..2856b1d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/scripts/native_solc_compile_all @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -e + +SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +solc-select install 0.6.6 +solc-select install 0.7.6 +solc-select install 0.8.6 + +$SCRIPTPATH/native_solc6_compile Flags.sol +$SCRIPTPATH/native_solc6_compile Oracle.sol +$SCRIPTPATH/native_solc6_compile FluxAggregator.sol +$SCRIPTPATH/native_solc6_compile VRFCoordinator.sol +$SCRIPTPATH/native_solc6_compile tests/VRFRequestIDBaseTestHelper.sol +$SCRIPTPATH/native_solc6_compile tests/VRFTestHelper.sol +$SCRIPTPATH/native_solc6_compile Chainlink.sol +$SCRIPTPATH/native_solc6_compile VRFRequestIDBase.sol +$SCRIPTPATH/native_solc6_compile tests/VRFConsumer.sol +$SCRIPTPATH/native_solc6_compile ChainlinkClient.sol +$SCRIPTPATH/native_solc6_compile VRFConsumerBase.sol + +$SCRIPTPATH/native_solc7_compile tests/MultiWordConsumer.sol +$SCRIPTPATH/native_solc7_compile dev/Operator.sol + +$SCRIPTPATH/native_solc8_compile dev/Chainlink.sol +$SCRIPTPATH/native_solc8_compile dev/ChainlinkClient.sol +$SCRIPTPATH/native_solc8_compile dev/VRFRequestIDBase.sol +$SCRIPTPATH/native_solc8_compile dev/VRFConsumerBase.sol +$SCRIPTPATH/native_solc8_compile tests/VRFConsumer.sol +$SCRIPTPATH/native_solc8_compile tests/VRFRequestIDBaseTestHelper.sol diff --git a/lib/chainlink-brownie-contracts/contracts/scripts/requirements.txt b/lib/chainlink-brownie-contracts/contracts/scripts/requirements.txt new file mode 100644 index 0000000..38d247c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/scripts/requirements.txt @@ -0,0 +1 @@ +solc-select==0.2.0 --hash=sha256:faa2fb5d43b77b6dc48ce73cb863b27631c477d0a350403294ff186c51bd744e diff --git a/lib/chainlink-brownie-contracts/contracts/scripts/test_ci_old b/lib/chainlink-brownie-contracts/contracts/scripts/test_ci_old new file mode 100755 index 0000000..e7e5e8b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/scripts/test_ci_old @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +yarn test "test/v0.4" +yarn test "test/v0.5" diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/Aggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Aggregator.sol new file mode 100644 index 0000000..69a1664 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Aggregator.sol @@ -0,0 +1,426 @@ +pragma solidity 0.4.24; + +import "./ChainlinkClient.sol"; +import "./interfaces/AggregatorInterface.sol"; +import "./vendor/SignedSafeMath.sol"; +import "./vendor/Ownable.sol"; +import "./vendor/SafeMathChainlink.sol"; + +/** + * @title An example Chainlink contract with aggregation + * @notice Requesters can use this contract as a framework for creating + * requests to multiple Chainlink nodes and running aggregation + * as the contract receives answers. + */ +contract Aggregator is AggregatorInterface, ChainlinkClient, Ownable { + using SafeMathChainlink for uint256; + using SignedSafeMath for int256; + + struct Answer { + uint128 minimumResponses; + uint128 maxResponses; + int256[] responses; + } + + event ResponseReceived(int256 indexed response, uint256 indexed answerId, address indexed sender); + + int256 private currentAnswerValue; + uint256 private updatedTimestampValue; + uint256 private latestCompletedAnswer; + uint128 public paymentAmount; + uint128 public minimumResponses; + bytes32[] public jobIds; + address[] public oracles; + + uint256 private answerCounter = 1; + mapping(address => bool) public authorizedRequesters; + mapping(bytes32 => uint256) private requestAnswers; + mapping(uint256 => Answer) private answers; + mapping(uint256 => int256) private currentAnswers; + mapping(uint256 => uint256) private updatedTimestamps; + + uint256 constant private MAX_ORACLE_COUNT = 28; + + /** + * @notice Deploy with the address of the LINK token and arrays of matching + * length containing the addresses of the oracles and their corresponding + * Job IDs. + * @dev Sets the LinkToken address for the network, addresses of the oracles, + * and jobIds in storage. + * @param _link The address of the LINK token + * @param _paymentAmount the amount of LINK to be sent to each oracle for each request + * @param _minimumResponses the minimum number of responses + * before an answer will be calculated + * @param _oracles An array of oracle addresses + * @param _jobIds An array of Job IDs + */ + constructor( + address _link, + uint128 _paymentAmount, + uint128 _minimumResponses, + address[] _oracles, + bytes32[] _jobIds + ) public Ownable() { + setChainlinkToken(_link); + updateRequestDetails(_paymentAmount, _minimumResponses, _oracles, _jobIds); + } + + /** + * @notice Creates a Chainlink request for each oracle in the oracles array. + * @dev This example does not include request parameters. Reference any documentation + * associated with the Job IDs used to determine the required parameters per-request. + */ + function requestRateUpdate() + external + ensureAuthorizedRequester() + { + Chainlink.Request memory request; + bytes32 requestId; + uint256 oraclePayment = paymentAmount; + + for (uint i = 0; i < oracles.length; i++) { + request = buildChainlinkRequest(jobIds[i], this, this.chainlinkCallback.selector); + requestId = sendChainlinkRequestTo(oracles[i], request, oraclePayment); + requestAnswers[requestId] = answerCounter; + } + answers[answerCounter].minimumResponses = minimumResponses; + answers[answerCounter].maxResponses = uint128(oracles.length); + + emit NewRound(answerCounter, msg.sender, block.timestamp); + + answerCounter = answerCounter.add(1); + } + + /** + * @notice Receives the answer from the Chainlink node. + * @dev This function can only be called by the oracle that received the request. + * @param _clRequestId The Chainlink request ID associated with the answer + * @param _response The answer provided by the Chainlink node + */ + function chainlinkCallback(bytes32 _clRequestId, int256 _response) + external + { + validateChainlinkCallback(_clRequestId); + + uint256 answerId = requestAnswers[_clRequestId]; + delete requestAnswers[_clRequestId]; + + answers[answerId].responses.push(_response); + emit ResponseReceived(_response, answerId, msg.sender); + updateLatestAnswer(answerId); + deleteAnswer(answerId); + } + + /** + * @notice Updates the arrays of oracles and jobIds with new values, + * overwriting the old values. + * @dev Arrays are validated to be equal length. + * @param _paymentAmount the amount of LINK to be sent to each oracle for each request + * @param _minimumResponses the minimum number of responses + * before an answer will be calculated + * @param _oracles An array of oracle addresses + * @param _jobIds An array of Job IDs + */ + function updateRequestDetails( + uint128 _paymentAmount, + uint128 _minimumResponses, + address[] _oracles, + bytes32[] _jobIds + ) + public + onlyOwner() + validateAnswerRequirements(_minimumResponses, _oracles, _jobIds) + { + paymentAmount = _paymentAmount; + minimumResponses = _minimumResponses; + jobIds = _jobIds; + oracles = _oracles; + } + + /** + * @notice Allows the owner of the contract to withdraw any LINK balance + * available on the contract. + * @dev The contract will need to have a LINK balance in order to create requests. + * @param _recipient The address to receive the LINK tokens + * @param _amount The amount of LINK to send from the contract + */ + function transferLINK(address _recipient, uint256 _amount) + public + onlyOwner() + { + LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress()); + require(linkToken.transfer(_recipient, _amount), "LINK transfer failed"); + } + + /** + * @notice Called by the owner to permission other addresses to generate new + * requests to oracles. + * @param _requester the address whose permissions are being set + * @param _allowed boolean that determines whether the requester is + * permissioned or not + */ + function setAuthorization(address _requester, bool _allowed) + external + onlyOwner() + { + authorizedRequesters[_requester] = _allowed; + } + + /** + * @notice Cancels an outstanding Chainlink request. + * The oracle contract requires the request ID and additional metadata to + * validate the cancellation. Only old answers can be cancelled. + * @param _requestId is the identifier for the chainlink request being cancelled + * @param _payment is the amount of LINK paid to the oracle for the request + * @param _expiration is the time when the request expires + */ + function cancelRequest( + bytes32 _requestId, + uint256 _payment, + uint256 _expiration + ) + external + ensureAuthorizedRequester() + { + uint256 answerId = requestAnswers[_requestId]; + require(answerId < latestCompletedAnswer, "Cannot modify an in-progress answer"); + + delete requestAnswers[_requestId]; + answers[answerId].responses.push(0); + deleteAnswer(answerId); + + cancelChainlinkRequest( + _requestId, + _payment, + this.chainlinkCallback.selector, + _expiration + ); + } + + /** + * @notice Called by the owner to kill the contract. This transfers all LINK + * balance and ETH balance (if there is any) to the owner. + */ + function destroy() + external + onlyOwner() + { + LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress()); + transferLINK(owner, linkToken.balanceOf(address(this))); + selfdestruct(owner); + } + + /** + * @dev Performs aggregation of the answers received from the Chainlink nodes. + * Assumes that at least half the oracles are honest and so can't control the + * middle of the ordered responses. + * @param _answerId The answer ID associated with the group of requests + */ + function updateLatestAnswer(uint256 _answerId) + private + ensureMinResponsesReceived(_answerId) + ensureOnlyLatestAnswer(_answerId) + { + uint256 responseLength = answers[_answerId].responses.length; + uint256 middleIndex = responseLength.div(2); + int256 currentAnswerTemp; + if (responseLength % 2 == 0) { + int256 median1 = quickselect(answers[_answerId].responses, middleIndex); + int256 median2 = quickselect(answers[_answerId].responses, middleIndex.add(1)); // quickselect is 1 indexed + currentAnswerTemp = median1.add(median2) / 2; // signed integers are not supported by SafeMath + } else { + currentAnswerTemp = quickselect(answers[_answerId].responses, middleIndex.add(1)); // quickselect is 1 indexed + } + currentAnswerValue = currentAnswerTemp; + latestCompletedAnswer = _answerId; + updatedTimestampValue = now; + updatedTimestamps[_answerId] = now; + currentAnswers[_answerId] = currentAnswerTemp; + emit AnswerUpdated(currentAnswerTemp, _answerId, now); + } + + /** + * @notice get the most recently reported answer + */ + function latestAnswer() + external + view + returns (int256) + { + return currentAnswers[latestCompletedAnswer]; + } + + /** + * @notice get the last updated at block timestamp + */ + function latestTimestamp() + external + view + returns (uint256) + { + return updatedTimestamps[latestCompletedAnswer]; + } + + /** + * @notice get past rounds answers + * @param _roundId the answer number to retrieve the answer for + */ + function getAnswer(uint256 _roundId) + external + view + returns (int256) + { + return currentAnswers[_roundId]; + } + + /** + * @notice get block timestamp when an answer was last updated + * @param _roundId the answer number to retrieve the updated timestamp for + */ + function getTimestamp(uint256 _roundId) + external + view + returns (uint256) + { + return updatedTimestamps[_roundId]; + } + + /** + * @notice get the latest completed round where the answer was updated + */ + function latestRound() + external + view + returns (uint256) + { + return latestCompletedAnswer; + } + + /** + * @dev Returns the kth value of the ordered array + * See: http://www.cs.yale.edu/homes/aspnes/pinewiki/QuickSelect.html + * @param _a The list of elements to pull from + * @param _k The index, 1 based, of the elements you want to pull from when ordered + */ + function quickselect(int256[] memory _a, uint256 _k) + private + pure + returns (int256) + { + int256[] memory a = _a; + uint256 k = _k; + uint256 aLen = a.length; + int256[] memory a1 = new int256[](aLen); + int256[] memory a2 = new int256[](aLen); + uint256 a1Len; + uint256 a2Len; + int256 pivot; + uint256 i; + + while (true) { + pivot = a[aLen.div(2)]; + a1Len = 0; + a2Len = 0; + for (i = 0; i < aLen; i++) { + if (a[i] < pivot) { + a1[a1Len] = a[i]; + a1Len++; + } else if (a[i] > pivot) { + a2[a2Len] = a[i]; + a2Len++; + } + } + if (k <= a1Len) { + aLen = a1Len; + (a, a1) = swap(a, a1); + } else if (k > (aLen.sub(a2Len))) { + k = k.sub(aLen.sub(a2Len)); + aLen = a2Len; + (a, a2) = swap(a, a2); + } else { + return pivot; + } + } + } + + /** + * @dev Swaps the pointers to two uint256 arrays in memory + * @param _a The pointer to the first in memory array + * @param _b The pointer to the second in memory array + */ + function swap(int256[] memory _a, int256[] memory _b) + private + pure + returns(int256[] memory, int256[] memory) + { + return (_b, _a); + } + + /** + * @dev Cleans up the answer record if all responses have been received. + * @param _answerId The identifier of the answer to be deleted + */ + function deleteAnswer(uint256 _answerId) + private + ensureAllResponsesReceived(_answerId) + { + delete answers[_answerId]; + } + + /** + * @dev Prevents taking an action if the minimum number of responses has not + * been received for an answer. + * @param _answerId The the identifier of the answer that keeps track of the responses. + */ + modifier ensureMinResponsesReceived(uint256 _answerId) { + if (answers[_answerId].responses.length >= answers[_answerId].minimumResponses) { + _; + } + } + + /** + * @dev Prevents taking an action if not all responses are received for an answer. + * @param _answerId The the identifier of the answer that keeps track of the responses. + */ + modifier ensureAllResponsesReceived(uint256 _answerId) { + if (answers[_answerId].responses.length == answers[_answerId].maxResponses) { + _; + } + } + + /** + * @dev Prevents taking an action if a newer answer has been recorded. + * @param _answerId The current answer's identifier. + * Answer IDs are in ascending order. + */ + modifier ensureOnlyLatestAnswer(uint256 _answerId) { + if (latestCompletedAnswer <= _answerId) { + _; + } + } + + /** + * @dev Ensures corresponding number of oracles and jobs. + * @param _oracles The list of oracles. + * @param _jobIds The list of jobs. + */ + modifier validateAnswerRequirements( + uint256 _minimumResponses, + address[] _oracles, + bytes32[] _jobIds + ) { + require(_oracles.length <= MAX_ORACLE_COUNT, "cannot have more than 45 oracles"); + require(_oracles.length >= _minimumResponses, "must have at least as many oracles as responses"); + require(_oracles.length == _jobIds.length, "must have exactly as many oracles as job IDs"); + _; + } + + /** + * @dev Reverts if `msg.sender` is not authorized to make requests. + */ + modifier ensureAuthorizedRequester() { + require(authorizedRequesters[msg.sender] || msg.sender == owner, "Not an authorized address for creating requests"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/Chainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Chainlink.sol new file mode 100644 index 0000000..1ab6e8b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Chainlink.sol @@ -0,0 +1,126 @@ +pragma solidity ^0.4.24; + +import { CBOR as CBOR_Chainlink } from "./vendor/CBOR.sol"; +import { Buffer as Buffer_Chainlink } from "./vendor/Buffer.sol"; + +/** + * @title Library for common Chainlink functions + * @dev Uses imported CBOR library for encoding to buffer + */ +library Chainlink { + uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase + + using CBOR_Chainlink for Buffer_Chainlink.buffer; + + struct Request { + bytes32 id; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + Buffer_Chainlink.buffer buf; + } + + /** + * @notice Initializes a Chainlink request + * @dev Sets the ID, callback address, and callback function signature on the request + * @param self The uninitialized request + * @param _id The Job Specification ID + * @param _callbackAddress The callback address + * @param _callbackFunction The callback function signature + * @return The initialized request + */ + function initialize( + Request memory self, + bytes32 _id, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (Chainlink.Request memory) { + Buffer_Chainlink.init(self.buf, defaultBufferSize); + self.id = _id; + self.callbackAddress = _callbackAddress; + self.callbackFunctionId = _callbackFunction; + return self; + } + + /** + * @notice Sets the data for the buffer without encoding CBOR on-chain + * @dev CBOR can be closed with curly-brackets {} or they can be left off + * @param self The initialized request + * @param _data The CBOR data + */ + function setBuffer(Request memory self, bytes _data) + internal pure + { + Buffer_Chainlink.init(self.buf, _data.length); + Buffer_Chainlink.append(self.buf, _data); + } + + /** + * @notice Adds a string value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The string value to add + */ + function add(Request memory self, string _key, string _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeString(_value); + } + + /** + * @notice Adds a bytes value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The bytes value to add + */ + function addBytes(Request memory self, string _key, bytes _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeBytes(_value); + } + + /** + * @notice Adds a int256 value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The int256 value to add + */ + function addInt(Request memory self, string _key, int256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeInt(_value); + } + + /** + * @notice Adds a uint256 value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The uint256 value to add + */ + function addUint(Request memory self, string _key, uint256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeUInt(_value); + } + + /** + * @notice Adds an array of strings to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _values The array of string values to add + */ + function addStringArray(Request memory self, string _key, string[] memory _values) + internal pure + { + self.buf.encodeString(_key); + self.buf.startArray(); + for (uint256 i = 0; i < _values.length; i++) { + self.buf.encodeString(_values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/ChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/ChainlinkClient.sol new file mode 100644 index 0000000..b62664c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/ChainlinkClient.sol @@ -0,0 +1,260 @@ +pragma solidity ^0.4.24; + +import "./Chainlink.sol"; +import "./interfaces/ENSInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/PointerInterface.sol"; +import { ENSResolver as ENSResolver_Chainlink } from "./vendor/ENSResolver.sol"; + +/** + * @title The ChainlinkClient contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network + */ +contract ChainlinkClient { + using Chainlink for Chainlink.Request; + + uint256 constant internal LINK = 10**18; + uint256 constant private AMOUNT_OVERRIDE = 0; + address constant private SENDER_OVERRIDE = 0x0; + uint256 constant private ARGS_VERSION = 1; + bytes32 constant private ENS_TOKEN_SUBNAME = keccak256("link"); + bytes32 constant private ENS_ORACLE_SUBNAME = keccak256("oracle"); + address constant private LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; + + ENSInterface private ens; + bytes32 private ensNode; + LinkTokenInterface private link; + ChainlinkRequestInterface private oracle; + uint256 private requests = 1; + mapping(bytes32 => address) private pendingRequests; + + event ChainlinkRequested(bytes32 indexed id); + event ChainlinkFulfilled(bytes32 indexed id); + event ChainlinkCancelled(bytes32 indexed id); + + /** + * @notice Creates a request that can hold additional parameters + * @param _specId The Job Specification ID that the request will be created for + * @param _callbackAddress The callback address that the response will be sent to + * @param _callbackFunctionSignature The callback function signature to use for the callback address + * @return A Chainlink Request struct in memory + */ + function buildChainlinkRequest( + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunctionSignature + ) internal pure returns (Chainlink.Request memory) { + Chainlink.Request memory req; + return req.initialize(_specId, _callbackAddress, _callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `chainlinkRequestTo` with the stored oracle address + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return The request ID + */ + function sendChainlinkRequest(Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32) + { + return sendChainlinkRequestTo(oracle, _req, _payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param _oracle The address of the oracle for the request + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return The request ID + */ + function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, requests)); + _req.nonce = requests; + pendingRequests[requestId] = _oracle; + emit ChainlinkRequested(requestId); + require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle"); + requests += 1; + + return requestId; + } + + /** + * @notice Allows a request to be cancelled if it has not been fulfilled + * @dev Requires keeping track of the expiration value emitted from the oracle contract. + * Deletes the request from the `pendingRequests` mapping. + * Emits ChainlinkCancelled event. + * @param _requestId The request ID + * @param _payment The amount of LINK sent for the request + * @param _callbackFunc The callback function specified for the request + * @param _expiration The time of the expiration for the request + */ + function cancelChainlinkRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunc, + uint256 _expiration + ) + internal + { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(pendingRequests[_requestId]); + delete pendingRequests[_requestId]; + emit ChainlinkCancelled(_requestId); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunc, _expiration); + } + + /** + * @notice Sets the stored oracle address + * @param _oracle The address of the oracle contract + */ + function setChainlinkOracle(address _oracle) internal { + oracle = ChainlinkRequestInterface(_oracle); + } + + /** + * @notice Sets the LINK token address + * @param _link The address of the LINK token contract + */ + function setChainlinkToken(address _link) internal { + link = LinkTokenInterface(_link); + } + + /** + * @notice Sets the Chainlink token address for the public + * network as given by the Pointer contract + */ + function setPublicChainlinkToken() internal { + setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkTokenAddress() + internal + view + returns (address) + { + return address(link); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function chainlinkOracleAddress() + internal + view + returns (address) + { + return address(oracle); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param _oracle The address of the oracle contract that will fulfill the request + * @param _requestId The request ID used for the response + */ + function addChainlinkExternalRequest(address _oracle, bytes32 _requestId) + internal + notPendingRequest(_requestId) + { + pendingRequests[_requestId] = _oracle; + } + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param _ens The address of the ENS contract + * @param _node The ENS node hash + */ + function useChainlinkWithENS(address _ens, bytes32 _node) + internal + { + ens = ENSInterface(_ens); + ensNode = _node; + bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ENS_TOKEN_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(linkSubnode)); + setChainlinkToken(resolver.addr(linkSubnode)); + updateChainlinkOracleWithENS(); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously + */ + function updateChainlinkOracleWithENS() + internal + { + bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(oracleSubnode)); + setChainlinkOracle(resolver.addr(oracleSubnode)); + } + + /** + * @notice Encodes the request to be sent to the oracle contract + * @dev The Chainlink node expects values to be in order for the request to be picked up. Order of types + * will be validated in the oracle contract. + * @param _req The initialized Chainlink Request + * @return The bytes payload for the `transferAndCall` method + */ + function encodeRequest(Chainlink.Request memory _req) + private + view + returns (bytes memory) + { + return abi.encodeWithSelector( + oracle.oracleRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + ARGS_VERSION, + _req.buf.buf); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param _requestId The request ID for fulfillment + */ + function validateChainlinkCallback(bytes32 _requestId) + internal + recordChainlinkFulfillment(_requestId) + // solhint-disable-next-line no-empty-blocks + {} + + /** + * @dev Reverts if the sender is not the oracle of the request. + * Emits ChainlinkFulfilled event. + * @param _requestId The request ID for fulfillment + */ + modifier recordChainlinkFulfillment(bytes32 _requestId) { + require(msg.sender == pendingRequests[_requestId], "Source must be the oracle of the request"); + delete pendingRequests[_requestId]; + emit ChainlinkFulfilled(_requestId); + _; + } + + /** + * @dev Reverts if the request is already pending + * @param _requestId The request ID for fulfillment + */ + modifier notPendingRequest(bytes32 _requestId) { + require(pendingRequests[_requestId] == address(0), "Request is already pending"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/Chainlinked.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Chainlinked.sol new file mode 100644 index 0000000..d537f9a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Chainlinked.sol @@ -0,0 +1,141 @@ +pragma solidity ^0.4.24; + +import "./ChainlinkClient.sol"; + +/** + * @title The Chainlinked contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network. ChainlinkClient is an alias of the Chainlinked contract. + */ +contract Chainlinked is ChainlinkClient { + /** + * @notice Creates a request that can hold additional parameters + * @param _specId The Job Specification ID that the request will be created for + * @param _callbackAddress The callback address that the response will be sent to + * @param _callbackFunctionSignature The callback function signature to use for the callback address + * @return A Chainlink Request struct in memory + */ + function newRequest( + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunctionSignature + ) internal pure returns (Chainlink.Request memory) { + return buildChainlinkRequest(_specId, _callbackAddress, _callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `sendChainlinkRequestTo` with the stored oracle address + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return The request ID + */ + function chainlinkRequest(Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32) + { + return sendChainlinkRequest(_req, _payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param _oracle The address of the oracle for the request + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return The request ID + */ + function chainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32 requestId) + { + return sendChainlinkRequestTo(_oracle, _req, _payment); + } + + /** + * @notice Sets the stored oracle address + * @param _oracle The address of the oracle contract + */ + function setOracle(address _oracle) internal { + setChainlinkOracle(_oracle); + } + + /** + * @notice Sets the LINK token address + * @param _link The address of the LINK token contract + */ + function setLinkToken(address _link) internal { + setChainlinkToken(_link); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkToken() + internal + view + returns (address) + { + return chainlinkTokenAddress(); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function oracleAddress() + internal + view + returns (address) + { + return chainlinkOracleAddress(); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param _requestId The request ID for fulfillment + */ + function fulfillChainlinkRequest(bytes32 _requestId) + internal + recordChainlinkFulfillment(_requestId) + // solhint-disable-next-line no-empty-blocks + {} + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param _ens The address of the ENS contract + * @param _node The ENS node hash + */ + function setChainlinkWithENS(address _ens, bytes32 _node) + internal + { + useChainlinkWithENS(_ens, _node); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `setChainlinkWithENS` has been called previously + */ + function setOracleWithENS() + internal + { + updateChainlinkOracleWithENS(); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param _oracle The address of the oracle contract that will fulfill the request + * @param _requestId The request ID used for the response + */ + function addExternalRequest(address _oracle, bytes32 _requestId) + internal + { + addChainlinkExternalRequest(_oracle, _requestId); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/ERC677Token.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/ERC677Token.sol new file mode 100644 index 0000000..d78ca1f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/ERC677Token.sol @@ -0,0 +1,47 @@ +pragma solidity ^0.4.11; + + +import "./interfaces/ERC677.sol"; +import "./interfaces/ERC677Receiver.sol"; + + +contract ERC677Token is ERC677 { + + /** + * @dev transfer token to a contract address with additional data if the recipient is a contact. + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + * @param _data The extra data to be passed to the receiving contract. + */ + function transferAndCall(address _to, uint _value, bytes _data) + public + returns (bool success) + { + super.transfer(_to, _value); + Transfer(msg.sender, _to, _value, _data); + if (isContract(_to)) { + contractFallback(_to, _value, _data); + } + return true; + } + + + // PRIVATE + + function contractFallback(address _to, uint _value, bytes _data) + private + { + ERC677Receiver receiver = ERC677Receiver(_to); + receiver.onTokenTransfer(msg.sender, _value, _data); + } + + function isContract(address _addr) + private + returns (bool hasCode) + { + uint length; + assembly { length := extcodesize(_addr) } + return length > 0; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/LinkToken.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/LinkToken.sol new file mode 100644 index 0000000..ba2e7ba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/LinkToken.sol @@ -0,0 +1,83 @@ +pragma solidity ^0.4.11; + + +import "./ERC677Token.sol"; +import { StandardToken as linkStandardToken } from "./vendor/StandardToken.sol"; + + +contract LinkToken is linkStandardToken, ERC677Token { + + uint public constant totalSupply = 10**27; + string public constant name = "ChainLink Token"; + uint8 public constant decimals = 18; + string public constant symbol = "LINK"; + + function LinkToken() + public + { + balances[msg.sender] = totalSupply; + } + + /** + * @dev transfer token to a specified address with additional data if the recipient is a contract. + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + * @param _data The extra data to be passed to the receiving contract. + */ + function transferAndCall(address _to, uint _value, bytes _data) + public + validRecipient(_to) + returns (bool success) + { + return super.transferAndCall(_to, _value, _data); + } + + /** + * @dev transfer token to a specified address. + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint _value) + public + validRecipient(_to) + returns (bool success) + { + return super.transfer(_to, _value); + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint256 _value) + public + validRecipient(_spender) + returns (bool) + { + return super.approve(_spender, _value); + } + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom(address _from, address _to, uint256 _value) + public + validRecipient(_to) + returns (bool) + { + return super.transferFrom(_from, _to, _value); + } + + + // MODIFIERS + + modifier validRecipient(address _recipient) { + require(_recipient != address(0) && _recipient != address(this)); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/Migrations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Migrations.sol new file mode 100644 index 0000000..4f01db5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.24; // solhint-disable-line compiler-fixed + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + constructor() public { + owner = msg.sender; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/Oracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Oracle.sol new file mode 100644 index 0000000..1ee9fcc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Oracle.sol @@ -0,0 +1,320 @@ +pragma solidity 0.4.24; + +import "./vendor/Ownable.sol"; +import "./vendor/SafeMathChainlink.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/OracleInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; + +/** + * @title The Chainlink Oracle contract + * @notice Node operators can deploy this contract to fulfill requests sent to them + */ +contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable { + using SafeMathChainlink for uint256; + + uint256 constant public EXPIRY_TIME = 5 minutes; + uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; + // We initialize fields to 1 instead of 0 so that the first invocation + // does not cost more gas. + uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; + uint256 constant private SELECTOR_LENGTH = 4; + uint256 constant private EXPECTED_REQUEST_WORDS = 2; + uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); + + LinkTokenInterface internal LinkToken; + mapping(bytes32 => bytes32) private commitments; + mapping(address => bool) private authorizedNodes; + uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; + + 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 Ownable() { + LinkToken = LinkTokenInterface(_link); // external but already deployed and unalterable + } + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` + * values to ensure correctness. Calls oracleRequest. + * @param _sender Address of the sender + * @param _amount Amount of LINK sent (specified in wei) + * @param _data Payload of the transaction + */ + function onTokenTransfer( + address _sender, + uint256 _amount, + bytes _data + ) + public + onlyLINK + validRequestLength(_data) + permittedFunctionsForLINK(_data) + { + assembly { // solhint-disable-line no-inline-assembly + mstore(add(_data, 36), _sender) // ensure correct sender is passed + mstore(add(_data, 68), _amount) // ensure correct amount is passed + } + // solhint-disable-next-line avoid-low-level-calls + require(address(this).delegatecall(_data), "Unable to create request"); // calls oracleRequest + } + + /** + * @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 _data + ) + external + onlyLINK + checkCallbackAddress(_callbackAddress) + { + bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); + require(commitments[requestId] == 0, "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + uint256 expiration = now.add(EXPIRY_TIME); + + commitments[requestId] = keccak256( + abi.encodePacked( + _payment, + _callbackAddress, + _callbackFunctionId, + expiration + ) + ); + + 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 _payment The payment amount that will be released for the oracle (specified in wei) + * @param _callbackAddress The callback address to call for fulfillment + * @param _callbackFunctionId The callback function ID to use for fulfillment + * @param _expiration The expiration that the node should respond by before the requester can cancel + * @param _data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest( + bytes32 _requestId, + uint256 _payment, + address _callbackAddress, + bytes4 _callbackFunctionId, + uint256 _expiration, + bytes32 _data + ) + external + onlyAuthorizedNode + isValidRequest(_requestId) + returns (bool) + { + bytes32 paramsHash = keccak256( + abi.encodePacked( + _payment, + _callbackAddress, + _callbackFunctionId, + _expiration + ) + ); + require(commitments[_requestId] == paramsHash, "Params do not match request ID"); + withdrawableTokens = withdrawableTokens.add(_payment); + 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 + return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solhint-disable-line avoid-low-level-calls + } + + /** + * @notice Use this to check if a node is authorized for fulfilling requests + * @param _node The address of the Chainlink node + * @return The authorization status of the node + */ + function getAuthorizationStatus(address _node) external view returns (bool) { + return authorizedNodes[_node]; + } + + /** + * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. + * @param _node The address of the Chainlink node + * @param _allowed Bool value to determine if the node can fulfill requests + */ + function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { + authorizedNodes[_node] = _allowed; + } + + /** + * @notice Allows the node operator to withdraw earned LINK to a given address + * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node + * @param _recipient The address to send the LINK token to + * @param _amount The amount to send (specified in wei) + */ + function withdraw(address _recipient, uint256 _amount) + external + onlyOwner + hasAvailableFunds(_amount) + { + withdrawableTokens = withdrawableTokens.sub(_amount); + assert(LinkToken.transfer(_recipient, _amount)); + } + + /** + * @notice Displays the amount of LINK that is available for the node operator to withdraw + * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage + * @return The amount of withdrawable LINK on the contract + */ + function withdrawable() external view onlyOwner returns (uint256) { + return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); + } + + /** + * @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 _callbackFunc The requester's specified callback address + * @param _expiration The time of the expiration for the request + */ + function cancelOracleRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunc, + uint256 _expiration + ) external { + bytes32 paramsHash = keccak256( + abi.encodePacked( + _payment, + msg.sender, + _callbackFunc, + _expiration) + ); + require(paramsHash == commitments[_requestId], "Params do not match request 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)); + } + + // MODIFIERS + + /** + * @dev Reverts if amount requested is greater than withdrawable balance + * @param _amount The given amount to compare to `withdrawableTokens` + */ + modifier hasAvailableFunds(uint256 _amount) { + require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); + _; + } + + /** + * @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] != 0, "Must have a valid requestId"); + _; + } + + /** + * @dev Reverts if `msg.sender` is not authorized to fulfill requests + */ + modifier onlyAuthorizedNode() { + require(authorizedNodes[msg.sender] || msg.sender == owner, "Not an authorized node to fulfill requests"); + _; + } + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + require(msg.sender == address(LinkToken), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `oracleRequest` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes _data) { + bytes4 funcSelector; + assembly { // solhint-disable-line no-inline-assembly + funcSelector := mload(add(_data, 32)) + } + require(funcSelector == this.oracleRequest.selector, "Must use whitelisted functions"); + _; + } + + /** + * @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"); + _; + } + + /** + * @dev Reverts if the given payload is less than needed to create a request + * @param _data The request payload + */ + modifier validRequestLength(bytes _data) { + require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/Pointer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Pointer.sol new file mode 100644 index 0000000..40ff35f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/Pointer.sol @@ -0,0 +1,9 @@ +pragma solidity 0.4.24; + +contract Pointer { + address public getAddress; + + constructor(address _addr) public { + getAddress = _addr; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/AggregatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/AggregatorInterface.sol new file mode 100644 index 0000000..d9eaf17 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/AggregatorInterface.sol @@ -0,0 +1,12 @@ +pragma solidity >=0.4.24; + +interface AggregatorInterface { + function latestAnswer() external view returns (int256); + function latestTimestamp() external view returns (uint256); + function latestRound() external view returns (uint256); + function getAnswer(uint256 roundId) external view returns (int256); + function getTimestamp(uint256 roundId) external view returns (uint256); + + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); + event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/AggregatorV3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/AggregatorV3Interface.sol new file mode 100644 index 0000000..f6b4849 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/AggregatorV3Interface.sol @@ -0,0 +1,33 @@ +pragma solidity >=0.4.24; + +interface AggregatorV3Interface { + + function decimals() external view returns (uint8); + function description() external view returns (string memory); + function version() external view returns (uint256); + + // getRoundData and latestRoundData should both raise "No data present" + // if they do not have data to report, instead of returning unset values + // which could be misinterpreted as actual reported values. + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ChainlinkRequestInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ChainlinkRequestInterface.sol new file mode 100644 index 0000000..d61c38e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ChainlinkRequestInterface.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.24; + +interface ChainlinkRequestInterface { + function oracleRequest( + address sender, + uint256 payment, + bytes32 id, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 version, + bytes data + ) external; + + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunctionId, + uint256 expiration + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ENSInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ENSInterface.sol new file mode 100644 index 0000000..f374a46 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ENSInterface.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.24; + +interface ENSInterface { + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external; + function setResolver(bytes32 node, address resolver) external; + function setOwner(bytes32 node, address owner) external; + function setTTL(bytes32 node, uint64 ttl) external; + function owner(bytes32 node) external view returns (address); + function resolver(bytes32 node) external view returns (address); + function ttl(bytes32 node) external view returns (uint64); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC20.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC20.sol new file mode 100644 index 0000000..fd978c3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC20.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.11; + + +import { ERC20Basic as linkERC20Basic } from "./ERC20Basic.sol"; + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20 is linkERC20Basic { + function allowance(address owner, address spender) constant returns (uint256); + function transferFrom(address from, address to, uint256 value) returns (bool); + function approve(address spender, uint256 value) returns (bool); + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC20Basic.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC20Basic.sol new file mode 100644 index 0000000..07ab02f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC20Basic.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.4.11; + + +/** + * @title ERC20Basic + * @dev Simpler version of ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/179 + */ +contract ERC20Basic { + uint256 public totalSupply; + function balanceOf(address who) constant returns (uint256); + function transfer(address to, uint256 value) returns (bool); + event Transfer(address indexed from, address indexed to, uint256 value); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC677.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC677.sol new file mode 100644 index 0000000..1e6714f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC677.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.8; + +import { ERC20 as linkERC20 } from "./ERC20.sol"; + +contract ERC677 is linkERC20 { + function transferAndCall(address to, uint value, bytes data) returns (bool success); + + event Transfer(address indexed from, address indexed to, uint value, bytes data); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC677Receiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC677Receiver.sol new file mode 100644 index 0000000..8a46d0b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/ERC677Receiver.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.4.8; + + +contract ERC677Receiver { + function onTokenTransfer(address _sender, uint _value, bytes _data); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/FlagsInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/FlagsInterface.sol new file mode 100644 index 0000000..5a0373e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/FlagsInterface.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.4.24; + +interface FlagsInterface { + function getFlag(address) external view returns (bool); + function getFlags(address[] calldata) external view returns (bool[] memory); + function raiseFlag(address) external; + function raiseFlags(address[] calldata) external; + function lowerFlags(address[] calldata) external; + function setRaisingAccessController(address) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/LinkTokenInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/LinkTokenInterface.sol new file mode 100644 index 0000000..d4f813c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/LinkTokenInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.24; + +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 tokenName); + function symbol() external view returns (string 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 data) external returns (bool success); + function transferFrom(address from, address to, uint256 value) external returns (bool success); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/OracleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/OracleInterface.sol new file mode 100644 index 0000000..9a32445 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/OracleInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.24; + +interface OracleInterface { + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) external returns (bool); + function getAuthorizationStatus(address node) external view returns (bool); + function setFulfillmentPermission(address node, bool allowed) external; + function withdraw(address recipient, uint256 amount) external; + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/PointerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/PointerInterface.sol new file mode 100644 index 0000000..ba0d224 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/interfaces/PointerInterface.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.24; + +interface PointerInterface { + function getAddress() external view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/BasicConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/BasicConsumer.sol new file mode 100644 index 0000000..42e5bfe --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/BasicConsumer.sol @@ -0,0 +1,13 @@ +pragma solidity 0.4.24; + +import "./Consumer.sol"; + +contract BasicConsumer is Consumer { + + constructor(address _link, address _oracle, bytes32 _specId) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/ConcreteChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/ConcreteChainlink.sol new file mode 100644 index 0000000..da4756d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/ConcreteChainlink.sol @@ -0,0 +1,77 @@ +pragma solidity 0.4.24; + +import "../Chainlink.sol"; +import { CBOR as CBOR_Chainlink } from "../vendor/CBOR.sol"; +import { Buffer as Buffer_Chainlink } from "../vendor/Buffer.sol"; + +contract ConcreteChainlink { + using Chainlink for Chainlink.Request; + using CBOR_Chainlink for Buffer_Chainlink.buffer; + + Chainlink.Request private req; + + event RequestData(bytes payload); + + function closeEvent() public { + emit RequestData(req.buf.buf); + } + + function setBuffer(bytes data) public { + Chainlink.Request memory r2 = req; + r2.setBuffer(data); + req = r2; + } + + function add(string _key, string _value) public { + Chainlink.Request memory r2 = req; + r2.add(_key, _value); + req = r2; + } + + function addBytes(string _key, bytes _value) public { + Chainlink.Request memory r2 = req; + r2.addBytes(_key, _value); + req = r2; + } + + function addInt(string _key, int256 _value) public { + Chainlink.Request memory r2 = req; + r2.addInt(_key, _value); + req = r2; + } + + function addUint(string _key, uint256 _value) public { + Chainlink.Request memory r2 = req; + r2.addUint(_key, _value); + req = r2; + } + + // Temporarily have method receive bytes32[] memory until experimental + // string[] memory can be invoked from truffle tests. + function addStringArray(string _key, bytes32[] memory _values) public { + string[] memory strings = new string[](_values.length); + for (uint256 i = 0; i < _values.length; i++) { + strings[i] = bytes32ToString(_values[i]); + } + Chainlink.Request memory r2 = req; + r2.addStringArray(_key, strings); + req = r2; + } + + function bytes32ToString(bytes32 x) private pure returns (string) { + bytes memory bytesString = new bytes(32); + uint charCount = 0; + for (uint j = 0; j < 32; j++) { + byte char = byte(bytes32(uint(x) * 2 ** (8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/ConcreteChainlinked.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/ConcreteChainlinked.sol new file mode 100644 index 0000000..bed787e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/ConcreteChainlinked.sol @@ -0,0 +1,101 @@ +pragma solidity 0.4.24; + +import "../Chainlinked.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract ConcreteChainlinked is Chainlinked { + using SafeMathChainlink for uint256; + + constructor(address _link, address _oracle) public { + setLinkToken(_link); + setOracle(_oracle); + } + + event Request( + bytes32 id, + address callbackAddress, + bytes4 callbackfunctionSelector, + bytes data + ); + + function publicNewRequest( + bytes32 _id, + address _address, + bytes _fulfillmentSignature + ) + public + { + Chainlink.Request memory req = newRequest( + _id, _address, bytes4(keccak256(_fulfillmentSignature))); + emit Request( + req.id, + req.callbackAddress, + req.callbackFunctionId, + req.buf.buf + ); + } + + function publicRequest( + bytes32 _id, + address _address, + bytes _fulfillmentSignature, + uint256 _wei + ) + public + { + Chainlink.Request memory req = newRequest( + _id, _address, bytes4(keccak256(_fulfillmentSignature))); + chainlinkRequest(req, _wei); + } + + function publicRequestRunTo( + address _oracle, + bytes32 _id, + address _address, + bytes _fulfillmentSignature, + uint256 _wei + ) + public + { + Chainlink.Request memory run = newRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + chainlinkRequestTo(_oracle, run, _wei); + } + + function publicCancelRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function publicChainlinkToken() public view returns (address) { + return chainlinkToken(); + } + + function fulfillRequest(bytes32 _requestId, bytes32) + public + recordChainlinkFulfillment(_requestId) + {} // solhint-disable-line no-empty-blocks + + function publicFulfillChainlinkRequest(bytes32 _requestId, bytes32) public { + fulfillChainlinkRequest(_requestId); + } + + event LinkAmount(uint256 amount); + + function publicLINK(uint256 _amount) public { + emit LinkAmount(LINK.mul(_amount)); + } + + function publicOracleAddress() public view returns (address) { + return oracleAddress(); + } + + function publicAddExternalRequest(address _oracle, bytes32 _requestId) + public + { + addExternalRequest(_oracle, _requestId); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/Consumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/Consumer.sol new file mode 100644 index 0000000..9fe29dd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/Consumer.sol @@ -0,0 +1,47 @@ +pragma solidity 0.4.24; + +import "../ChainlinkClient.sol"; + +contract Consumer is ChainlinkClient { + bytes32 internal specId; + bytes32 public currentPrice; + + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + + event RequestFulfilled( + bytes32 indexed requestId, // User-defined ID + bytes32 indexed price + ); + + function requestEthereumPrice(string _currency) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, this, this.fulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = _currency; + req.addStringArray("path", path); + sendChainlinkRequest(req, ORACLE_PAYMENT); + } + + function cancelRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function withdrawLink() public { + LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress()); + require(link.transfer(msg.sender, link.balanceOf(address(this))), "Unable to transfer"); + } + + function fulfill(bytes32 _requestId, bytes32 _price) + public + recordChainlinkFulfillment(_requestId) + { + emit RequestFulfilled(_requestId, _price); + currentPrice = _price; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/EmptyOracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/EmptyOracle.sol new file mode 100644 index 0000000..b437ef6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/EmptyOracle.sol @@ -0,0 +1,19 @@ +pragma solidity 0.4.24; + +import "../interfaces/ChainlinkRequestInterface.sol"; +import "../interfaces/OracleInterface.sol"; + +/* solhint-disable no-empty-blocks */ + +contract EmptyOracle is ChainlinkRequestInterface, OracleInterface { + + function cancelOracleRequest(bytes32, uint256, bytes4, uint256) external {} + function fulfillOracleRequest(bytes32, uint256, address, bytes4, uint256, bytes32) external returns (bool) {} + function getAuthorizationStatus(address) external view returns (bool) { return false; } + function onTokenTransfer(address, uint256, bytes) external pure {} + function oracleRequest(address, uint256, bytes32, address, bytes4, uint256, uint256, bytes) external {} + function setFulfillmentPermission(address, bool) external {} + function withdraw(address, uint256) external {} + function withdrawable() external view returns (uint256) {} + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/GetterSetter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/GetterSetter.sol new file mode 100644 index 0000000..fcd86c7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/GetterSetter.sol @@ -0,0 +1,45 @@ +pragma solidity 0.4.24; + +// GetterSetter is a contract to aid debugging and testing during development. +contract GetterSetter { + bytes32 public getBytes32; + uint256 public getUint256; + bytes32 public requestId; + bytes public getBytes; + + event SetBytes32(address indexed from, bytes32 indexed value); + event SetUint256(address indexed from, uint256 indexed value); + event SetBytes(address indexed from, bytes value); + + event Output(bytes32 b32, uint256 u256, bytes32 b322); + + function setBytes32(bytes32 _value) public { + getBytes32 = _value; + emit SetBytes32(msg.sender, _value); + } + + function requestedBytes32(bytes32 _requestId, bytes32 _value) public { + requestId = _requestId; + setBytes32(_value); + } + + function setBytes(bytes _value) public { + getBytes = _value; + emit SetBytes(msg.sender, _value); + } + + function requestedBytes(bytes32 _requestId, bytes _value) public { + requestId = _requestId; + setBytes(_value); + } + + function setUint256(uint256 _value) public { + getUint256 = _value; + emit SetUint256(msg.sender, _value); + } + + function requestedUint256(bytes32 _requestId, uint256 _value) public { + requestId = _requestId; + setUint256(_value); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousChainlink.sol new file mode 100644 index 0000000..8a0d048 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousChainlink.sol @@ -0,0 +1,76 @@ +pragma solidity 0.4.24; + +import { CBOR as CBOR_Chainlink } from "../vendor/CBOR.sol"; +import { Buffer as Buffer_Chainlink } from "../vendor/Buffer.sol"; + +library MaliciousChainlink { + using CBOR_Chainlink for Buffer_Chainlink.buffer; + + struct Request { + bytes32 specId; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + Buffer_Chainlink.buffer buf; + } + + struct WithdrawRequest { + bytes32 specId; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + Buffer_Chainlink.buffer buf; + } + + function initializeWithdraw( + WithdrawRequest memory self, + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (MaliciousChainlink.WithdrawRequest memory) { + Buffer_Chainlink.init(self.buf, 128); + self.specId = _specId; + self.callbackAddress = _callbackAddress; + self.callbackFunctionId = _callbackFunction; + return self; + } + + function add(Request memory self, string _key, string _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeString(_value); + } + + function addBytes(Request memory self, string _key, bytes _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeBytes(_value); + } + + function addInt(Request memory self, string _key, int256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeInt(_value); + } + + function addUint(Request memory self, string _key, uint256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeUInt(_value); + } + + function addStringArray(Request memory self, string _key, string[] memory _values) + internal pure + { + self.buf.encodeString(_key); + self.buf.startArray(); + for (uint256 i = 0; i < _values.length; i++) { + self.buf.encodeString(_values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousChainlinked.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousChainlinked.sol new file mode 100644 index 0000000..b5b8795 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousChainlinked.sol @@ -0,0 +1,109 @@ +pragma solidity 0.4.24; + +import "./MaliciousChainlink.sol"; +import "../Chainlinked.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract MaliciousChainlinked is Chainlinked { + using MaliciousChainlink for MaliciousChainlink.Request; + using MaliciousChainlink for MaliciousChainlink.WithdrawRequest; + using Chainlink for Chainlink.Request; + using SafeMathChainlink for uint256; + + uint256 private maliciousRequests = 1; + mapping(bytes32 => address) private maliciousPendingRequests; + + function newWithdrawRequest( + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (MaliciousChainlink.WithdrawRequest memory) { + MaliciousChainlink.WithdrawRequest memory req; + return req.initializeWithdraw(_specId, _callbackAddress, _callbackFunction); + } + + function chainlinkTargetRequest(address _target, Chainlink.Request memory _req, uint256 _amount) + internal + returns(bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(_target, maliciousRequests)); + _req.nonce = maliciousRequests; + maliciousPendingRequests[requestId] = oracleAddress(); + emit ChainlinkRequested(requestId); + LinkTokenInterface link = LinkTokenInterface(chainlinkToken()); + require(link.transferAndCall(oracleAddress(), _amount, encodeTargetRequest(_req)), "Unable to transferAndCall to oracle"); + maliciousRequests += 1; + + return requestId; + } + + function chainlinkPriceRequest(Chainlink.Request memory _req, uint256 _amount) + internal + returns(bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, maliciousRequests)); + _req.nonce = maliciousRequests; + maliciousPendingRequests[requestId] = oracleAddress(); + emit ChainlinkRequested(requestId); + LinkTokenInterface link = LinkTokenInterface(chainlinkToken()); + require(link.transferAndCall(oracleAddress(), _amount, encodePriceRequest(_req)), "Unable to transferAndCall to oracle"); + maliciousRequests += 1; + + return requestId; + } + + function chainlinkWithdrawRequest(MaliciousChainlink.WithdrawRequest memory _req, uint256 _wei) + internal + returns(bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, maliciousRequests)); + _req.nonce = maliciousRequests; + maliciousPendingRequests[requestId] = oracleAddress(); + emit ChainlinkRequested(requestId); + LinkTokenInterface link = LinkTokenInterface(chainlinkToken()); + require(link.transferAndCall(oracleAddress(), _wei, encodeWithdrawRequest(_req)), "Unable to transferAndCall to oracle"); + maliciousRequests += 1; + return requestId; + } + + function encodeWithdrawRequest(MaliciousChainlink.WithdrawRequest memory _req) + internal pure returns (bytes memory) + { + return abi.encodeWithSelector( + bytes4(keccak256("withdraw(address,uint256)")), + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + _req.buf.buf); + } + + function encodeTargetRequest(Chainlink.Request memory _req) + internal pure returns (bytes memory) + { + return abi.encodeWithSelector( + bytes4(keccak256("oracleRequest(address,uint256,bytes32,address,bytes4,uint256,uint256,bytes)")), + 0, // overridden by onTokenTransfer + 0, // overridden by onTokenTransfer + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + 1, + _req.buf.buf); + } + + function encodePriceRequest(Chainlink.Request memory _req) + internal pure returns (bytes memory) + { + return abi.encodeWithSelector( + bytes4(keccak256("oracleRequest(address,uint256,bytes32,address,bytes4,uint256,uint256,bytes)")), + 0, // overridden by onTokenTransfer + 2000000000000000000, // overridden by onTokenTransfer + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + 1, + _req.buf.buf); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousConsumer.sol new file mode 100644 index 0000000..b7b41d6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousConsumer.sol @@ -0,0 +1,57 @@ +pragma solidity 0.4.24; + + +import "../Chainlinked.sol"; +import "../vendor/SafeMathChainlink.sol"; + + +contract MaliciousConsumer is Chainlinked { + using SafeMathChainlink for uint256; + + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + uint256 private expiration; + + constructor(address _link, address _oracle) public payable { + setLinkToken(_link); + setOracle(_oracle); + } + + function () public payable {} // solhint-disable-line no-empty-blocks + + function requestData(bytes32 _id, bytes _callbackFunc) public { + Chainlink.Request memory req = newRequest(_id, this, bytes4(keccak256(_callbackFunc))); + expiration = now.add(5 minutes); // solhint-disable-line not-rely-on-time + chainlinkRequest(req, ORACLE_PAYMENT); + } + + function assertFail(bytes32, bytes32) public pure { + assert(1 == 2); + } + + function cancelRequestOnFulfill(bytes32 _requestId, bytes32) public { + cancelChainlinkRequest( + _requestId, + ORACLE_PAYMENT, + this.cancelRequestOnFulfill.selector, + expiration); + } + + function remove() public { + selfdestruct(address(0)); + } + + function stealEthCall(bytes32 _requestId, bytes32) public recordChainlinkFulfillment(_requestId) { + require(address(this).call.value(100)(), "Call failed"); // solhint-disable-line avoid-call-value + } + + function stealEthSend(bytes32 _requestId, bytes32) public recordChainlinkFulfillment(_requestId) { + // solhint-disable-next-line check-send-result + require(address(this).send(100), "Send failed"); // solhint-disable-line multiple-sends + } + + function stealEthTransfer(bytes32 _requestId, bytes32) public recordChainlinkFulfillment(_requestId) { + address(this).transfer(100); + } + + function doesNothing(bytes32, bytes32) public pure {} // solhint-disable-line no-empty-blocks +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousRequester.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousRequester.sol new file mode 100644 index 0000000..ae6468f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/MaliciousRequester.sol @@ -0,0 +1,52 @@ +pragma solidity 0.4.24; + + +import "./MaliciousChainlinked.sol"; + + +contract MaliciousRequester is MaliciousChainlinked { + + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + uint256 private expiration; + + constructor(address _link, address _oracle) public { + setLinkToken(_link); + setOracle(_oracle); + } + + function maliciousWithdraw() + public + { + MaliciousChainlink.WithdrawRequest memory req = newWithdrawRequest( + "specId", this, this.doesNothing.selector); + chainlinkWithdrawRequest(req, ORACLE_PAYMENT); + } + + function request(bytes32 _id, address _target, bytes _callbackFunc) public returns (bytes32 requestId) { + Chainlink.Request memory req = newRequest(_id, _target, bytes4(keccak256(_callbackFunc))); + expiration = now.add(5 minutes); // solhint-disable-line not-rely-on-time + requestId = chainlinkRequest(req, ORACLE_PAYMENT); + } + + function maliciousPrice(bytes32 _id) public returns (bytes32 requestId) { + Chainlink.Request memory req = newRequest(_id, this, this.doesNothing.selector); + requestId = chainlinkPriceRequest(req, ORACLE_PAYMENT); + } + + function maliciousTargetConsumer(address _target) public returns (bytes32 requestId) { + Chainlink.Request memory req = newRequest("specId", _target, bytes4(keccak256("fulfill(bytes32,bytes32)"))); + requestId = chainlinkTargetRequest(_target, req, ORACLE_PAYMENT); + } + + function maliciousRequestCancel(bytes32 _id, bytes _callbackFunc) public { + ChainlinkRequestInterface oracle = ChainlinkRequestInterface(oracleAddress()); + oracle.cancelOracleRequest( + request(_id, this, _callbackFunc), + ORACLE_PAYMENT, + this.maliciousRequestCancel.selector, + expiration + ); + } + + function doesNothing(bytes32, bytes32) public pure {} // solhint-disable-line no-empty-blocks +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/UpdatableConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/UpdatableConsumer.sol new file mode 100644 index 0000000..933dbcb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/tests/UpdatableConsumer.sol @@ -0,0 +1,24 @@ +pragma solidity 0.4.24; + +import "./Consumer.sol"; + +contract UpdatableConsumer is Consumer { + + constructor(bytes32 _specId, address _ens, bytes32 _node) public { + specId = _specId; + useChainlinkWithENS(_ens, _node); + } + + function updateOracle() public { + updateChainlinkOracleWithENS(); + } + + function getChainlinkToken() public view returns (address) { + return chainlinkTokenAddress(); + } + + function getOracle() public view returns (address) { + return chainlinkOracleAddress(); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/BasicToken.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/BasicToken.sol new file mode 100644 index 0000000..48f9850 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/BasicToken.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.4.24; + + +import { ERC20Basic as linkERC20Basic } from "../interfaces/ERC20Basic.sol"; +import { SafeMathChainlink as linkSafeMath } from "./SafeMathChainlink.sol"; + + +/** + * @title Basic token + * @dev Basic version of StandardToken, with no allowances. + */ +contract BasicToken is linkERC20Basic { + using linkSafeMath for uint256; + + mapping(address => uint256) balances; + + /** + * @dev transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint256 _value) returns (bool) { + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + Transfer(msg.sender, _to, _value); + return true; + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address _owner) constant returns (uint256 balance) { + return balances[_owner]; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/Buffer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/Buffer.sol new file mode 100644 index 0000000..d25ae7c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/Buffer.sol @@ -0,0 +1,301 @@ +pragma solidity >0.4.18; + +/** +* @dev A library for working with mutable byte buffers in Solidity. +* +* Byte buffers are mutable and expandable, and provide a variety of primitives +* for writing to them. At any time you can fetch a bytes object containing the +* current contents of the buffer. The bytes object should not be stored between +* operations, as it may change due to resizing of the buffer. +*/ +library Buffer { + /** + * @dev Represents a mutable buffer. Buffers have a current value (buf) and + * a capacity. The capacity may be longer than the current value, in + * which case it can be extended without the need to allocate more memory. + */ + struct buffer { + bytes buf; + uint capacity; + } + + /** + * @dev Initializes a buffer with an initial capacity. + * @param buf The buffer to initialize. + * @param capacity The number of bytes of space to allocate the buffer. + * @return The buffer, for chaining. + */ + function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + // Allocate space for the buffer data + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + mstore(0x40, add(32, add(ptr, capacity))) + } + return buf; + } + + /** + * @dev Initializes a new buffer from an existing bytes object. + * Changes to the buffer may mutate the original value. + * @param b The bytes object to initialize the buffer with. + * @return A new buffer. + */ + function fromBytes(bytes memory b) internal pure returns(buffer memory) { + buffer memory buf; + buf.buf = b; + buf.capacity = b.length; + return buf; + } + + function resize(buffer memory buf, uint capacity) private pure { + bytes memory oldbuf = buf.buf; + init(buf, capacity); + append(buf, oldbuf); + } + + function max(uint a, uint b) private pure returns(uint) { + if (a > b) { + return a; + } + return b; + } + + /** + * @dev Sets buffer length to 0. + * @param buf The buffer to truncate. + * @return The original buffer, for chaining.. + */ + function truncate(buffer memory buf) internal pure returns (buffer memory) { + assembly { + let bufptr := mload(buf) + mstore(bufptr, 0) + } + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) { + require(len <= data.length); + + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint dest; + uint src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(data, 32) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + + return buf; + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, len); + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, data.length); + } + + /** + * @dev Writes a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write the byte at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) { + if (off >= buf.capacity) { + resize(buf, buf.capacity * 2); + } + + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Address = buffer address + sizeof(buffer length) + off + let dest := add(add(bufptr, off), 32) + mstore8(dest, data) + // Update buffer length if we extended it + if eq(off, buflen) { + mstore(bufptr, add(buflen, 1)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) { + return writeUint8(buf, buf.buf.length, data); + } + + /** + * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (left-aligned). + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint mask = 256 ** len - 1; + // Right-align data + data = data >> (8 * (32 - len)); + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + sizeof(buffer length) + off + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) { + return write(buf, off, bytes32(data), 20); + } + + /** + * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chhaining. + */ + function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, bytes32(data), 20); + } + + /** + * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, 32); + } + + /** + * @dev Writes an integer to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (right-aligned). + * @return The original buffer, for chaining. + */ + function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint mask = 256 ** len - 1; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + off + sizeof(buffer length) + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer. + */ + function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { + return writeInt(buf, buf.buf.length, data, len); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/CBOR.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/CBOR.sol new file mode 100644 index 0000000..9cce04a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/CBOR.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity >= 0.4.19 < 0.7.0; + +import { Buffer as BufferChainlink } from "./Buffer.sol"; + +library CBOR { + using BufferChainlink for BufferChainlink.buffer; + + uint8 private constant MAJOR_TYPE_INT = 0; + uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 private constant MAJOR_TYPE_BYTES = 2; + uint8 private constant MAJOR_TYPE_STRING = 3; + uint8 private constant MAJOR_TYPE_ARRAY = 4; + uint8 private constant MAJOR_TYPE_MAP = 5; + uint8 private constant MAJOR_TYPE_TAG = 6; + uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; + + uint8 private constant TAG_TYPE_BIGNUM = 2; + uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; + + function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure { + if(value <= 23) { + buf.appendUint8(uint8((major << 5) | value)); + } else if(value <= 0xFF) { + buf.appendUint8(uint8((major << 5) | 24)); + buf.appendInt(value, 1); + } else if(value <= 0xFFFF) { + buf.appendUint8(uint8((major << 5) | 25)); + buf.appendInt(value, 2); + } else if(value <= 0xFFFFFFFF) { + buf.appendUint8(uint8((major << 5) | 26)); + buf.appendInt(value, 4); + } else { + buf.appendUint8(uint8((major << 5) | 27)); + buf.appendInt(value, 8); + } + } + + function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure { + buf.appendUint8(uint8((major << 5) | 31)); + } + + function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure { + if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, value); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } + } + + function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure { + if(value < -0x10000000000000000) { + encodeSignedBigNum(buf, value); + } else if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, uint(value)); + } else if(value >= 0) { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(-1 - value)); + } + } + + function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); + buf.append(value); + } + + function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); + encodeBytes(buf, abi.encode(value)); + } + + function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); + encodeBytes(buf, abi.encode(uint(-1 - input))); + } + + function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); + buf.append(bytes(value)); + } + + function startArray(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); + } + + function startMap(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); + } + + function endSequence(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENS.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENS.sol new file mode 100644 index 0000000..36e4ad4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENS.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.24; + +interface ENS { + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public; + function setResolver(bytes32 node, address resolver) public; + function setOwner(bytes32 node, address owner) public; + function setTTL(bytes32 node, uint64 ttl) public; + function owner(bytes32 node) public view returns (address); + function resolver(bytes32 node) public view returns (address); + function ttl(bytes32 node) public view returns (uint64); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENSRegistry.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENSRegistry.sol new file mode 100644 index 0000000..95a54cd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENSRegistry.sol @@ -0,0 +1,99 @@ +pragma solidity ^0.4.24; + +import "./ENS.sol"; + +/** + * The ENS registry contract. + */ +contract ENSRegistry is ENS { + struct Record { + address owner; + address resolver; + uint64 ttl; + } + + mapping (bytes32 => Record) records; + + // Permits modifications only by the owner of the specified node. + modifier only_owner(bytes32 node) { + require(records[node].owner == msg.sender); + _; + } + + /** + * @dev Constructs a new ENS registrar. + */ + constructor() public { + records[0x0].owner = msg.sender; + } + + /** + * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. + * @param node The node to transfer ownership of. + * @param owner The address of the new owner. + */ + function setOwner(bytes32 node, address owner) public only_owner(node) { + emit Transfer(node, owner); + records[node].owner = owner; + } + + /** + * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. + * @param node The parent node. + * @param label The hash of the label specifying the subnode. + * @param owner The address of the new owner. + */ + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public only_owner(node) { + bytes32 subnode = keccak256(abi.encodePacked(node, label)); + emit NewOwner(node, label, owner); + records[subnode].owner = owner; + } + + /** + * @dev Sets the resolver address for the specified node. + * @param node The node to update. + * @param resolver The address of the resolver. + */ + function setResolver(bytes32 node, address resolver) public only_owner(node) { + emit NewResolver(node, resolver); + records[node].resolver = resolver; + } + + /** + * @dev Sets the TTL for the specified node. + * @param node The node to update. + * @param ttl The TTL in seconds. + */ + function setTTL(bytes32 node, uint64 ttl) public only_owner(node) { + emit NewTTL(node, ttl); + records[node].ttl = ttl; + } + + /** + * @dev Returns the address that owns the specified node. + * @param node The specified node. + * @return address of the owner. + */ + function owner(bytes32 node) public view returns (address) { + return records[node].owner; + } + + /** + * @dev Returns the address of the resolver for the specified node. + * @param node The specified node. + * @return address of the resolver. + */ + function resolver(bytes32 node) public view returns (address) { + return records[node].resolver; + } + + /** + * @dev Returns the TTL of a node, and any records associated with it. + * @param node The specified node. + * @return ttl of the node. + */ + function ttl(bytes32 node) public view returns (uint64) { + return records[node].ttl; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENSResolver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENSResolver.sol new file mode 100644 index 0000000..c5149bf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/ENSResolver.sol @@ -0,0 +1,5 @@ +pragma solidity 0.4.24; + +contract ENSResolver { + function addr(bytes32 node) public view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/Ownable.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/Ownable.sol new file mode 100644 index 0000000..1fbf571 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/Ownable.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.4.24; + + +/** + * @title Ownable + * @dev The Ownable contract has an owner address, and provides basic authorization control + * functions, this simplifies the implementation of "user permissions". + */ +contract Ownable { + address public owner; + + + event OwnershipRenounced(address indexed previousOwner); + event OwnershipTransferred( + address indexed previousOwner, + address indexed newOwner + ); + + + /** + * @dev The Ownable constructor sets the original `owner` of the contract to the sender + * account. + */ + constructor() public { + owner = msg.sender; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + /** + * @dev Allows the current owner to relinquish control of the contract. + * @notice Renouncing to ownership will leave the contract without an owner. + * It will not be possible to call the functions with the `onlyOwner` + * modifier anymore. + */ + function renounceOwnership() public onlyOwner { + emit OwnershipRenounced(owner); + owner = address(0); + } + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param _newOwner The address to transfer ownership to. + */ + function transferOwnership(address _newOwner) public onlyOwner { + _transferOwnership(_newOwner); + } + + /** + * @dev Transfers control of the contract to a newOwner. + * @param _newOwner The address to transfer ownership to. + */ + function _transferOwnership(address _newOwner) internal { + require(_newOwner != address(0)); + emit OwnershipTransferred(owner, _newOwner); + owner = _newOwner; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/PublicResolver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/PublicResolver.sol new file mode 100644 index 0000000..50a01fd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/PublicResolver.sol @@ -0,0 +1,238 @@ +pragma solidity ^0.4.24; + +import "./ENS.sol"; + +/** + * A simple resolver anyone can use; only allows the owner of a node to set its + * address. + */ +contract PublicResolver { + + bytes4 constant INTERFACE_META_ID = 0x01ffc9a7; + bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de; + bytes4 constant CONTENT_INTERFACE_ID = 0xd8389dc5; + bytes4 constant NAME_INTERFACE_ID = 0x691f3431; + bytes4 constant ABI_INTERFACE_ID = 0x2203ab56; + bytes4 constant PUBKEY_INTERFACE_ID = 0xc8690233; + bytes4 constant TEXT_INTERFACE_ID = 0x59d1d43c; + bytes4 constant MULTIHASH_INTERFACE_ID = 0xe89401a1; + + event AddrChanged(bytes32 indexed node, address a); + event ContentChanged(bytes32 indexed node, bytes32 hash); + event NameChanged(bytes32 indexed node, string name); + event ABIChanged(bytes32 indexed node, uint256 indexed contentType); + event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); + event TextChanged(bytes32 indexed node, string indexedKey, string key); + event MultihashChanged(bytes32 indexed node, bytes hash); + + struct PublicKey { + bytes32 x; + bytes32 y; + } + + struct Record { + address addr; + bytes32 content; + string name; + PublicKey pubkey; + mapping(string=>string) text; + mapping(uint256=>bytes) abis; + bytes multihash; + } + + ENS ens; + + mapping (bytes32 => Record) records; + + modifier only_owner(bytes32 node) { + require(ens.owner(node) == msg.sender); + _; + } + + /** + * Constructor. + * @param ensAddr The ENS registrar contract. + */ + constructor(ENS ensAddr) public { + ens = ensAddr; + } + + /** + * Sets the address associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param addr The address to set. + */ + function setAddr(bytes32 node, address addr) public only_owner(node) { + records[node].addr = addr; + emit AddrChanged(node, addr); + } + + /** + * Sets the content hash associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * Note that this resource type is not standardized, and will likely change + * in future to a resource type based on multihash. + * @param node The node to update. + * @param hash The content hash to set + */ + function setContent(bytes32 node, bytes32 hash) public only_owner(node) { + records[node].content = hash; + emit ContentChanged(node, hash); + } + + /** + * Sets the multihash associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param hash The multihash to set + */ + function setMultihash(bytes32 node, bytes hash) public only_owner(node) { + records[node].multihash = hash; + emit MultihashChanged(node, hash); + } + + /** + * Sets the name associated with an ENS node, for reverse records. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param name The name to set. + */ + function setName(bytes32 node, string name) public only_owner(node) { + records[node].name = name; + emit NameChanged(node, name); + } + + /** + * Sets the ABI associated with an ENS node. + * Nodes may have one ABI of each content type. To remove an ABI, set it to + * the empty string. + * @param node The node to update. + * @param contentType The content type of the ABI + * @param data The ABI data. + */ + function setABI(bytes32 node, uint256 contentType, bytes data) public only_owner(node) { + // Content types must be powers of 2 + require(((contentType - 1) & contentType) == 0); + + records[node].abis[contentType] = data; + emit ABIChanged(node, contentType); + } + + /** + * Sets the SECP256k1 public key associated with an ENS node. + * @param node The ENS node to query + * @param x the X coordinate of the curve point for the public key. + * @param y the Y coordinate of the curve point for the public key. + */ + function setPubkey(bytes32 node, bytes32 x, bytes32 y) public only_owner(node) { + records[node].pubkey = PublicKey(x, y); + emit PubkeyChanged(node, x, y); + } + + /** + * Sets the text data associated with an ENS node and key. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param key The key to set. + * @param value The text data value to set. + */ + function setText(bytes32 node, string key, string value) public only_owner(node) { + records[node].text[key] = value; + emit TextChanged(node, key, key); + } + + /** + * Returns the text data associated with an ENS node and key. + * @param node The ENS node to query. + * @param key The text data key to query. + * @return The associated text data. + */ + function text(bytes32 node, string key) public view returns (string) { + return records[node].text[key]; + } + + /** + * Returns the SECP256k1 public key associated with an ENS node. + * Defined in EIP 619. + * @param node The ENS node to query + * @return x, y the X and Y coordinates of the curve point for the public key. + */ + function pubkey(bytes32 node) public view returns (bytes32 x, bytes32 y) { + return (records[node].pubkey.x, records[node].pubkey.y); + } + + /** + * Returns the ABI associated with an ENS node. + * Defined in EIP205. + * @param node The ENS node to query + * @param contentTypes A bitwise OR of the ABI formats accepted by the caller. + * @return contentType The content type of the return value + * @return data The ABI data + */ + function ABI(bytes32 node, uint256 contentTypes) public view returns (uint256 contentType, bytes data) { + Record storage record = records[node]; + for (contentType = 1; contentType <= contentTypes; contentType <<= 1) { + if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) { + data = record.abis[contentType]; + return; + } + } + contentType = 0; + } + + /** + * Returns the name associated with an ENS node, for reverse records. + * Defined in EIP181. + * @param node The ENS node to query. + * @return The associated name. + */ + function name(bytes32 node) public view returns (string) { + return records[node].name; + } + + /** + * Returns the content hash associated with an ENS node. + * Note that this resource type is not standardized, and will likely change + * in future to a resource type based on multihash. + * @param node The ENS node to query. + * @return The associated content hash. + */ + function content(bytes32 node) public view returns (bytes32) { + return records[node].content; + } + + /** + * Returns the multihash associated with an ENS node. + * @param node The ENS node to query. + * @return The associated multihash. + */ + function multihash(bytes32 node) public view returns (bytes) { + return records[node].multihash; + } + + /** + * Returns the address associated with an ENS node. + * @param node The ENS node to query. + * @return The associated address. + */ + function addr(bytes32 node) public view returns (address) { + return records[node].addr; + } + + /** + * Returns true if the resolver implements the interface specified by the provided hash. + * @param interfaceID The ID of the interface to check for. + * @return True if the contract implements the requested interface. + */ + function supportsInterface(bytes4 interfaceID) public pure returns (bool) { + return interfaceID == ADDR_INTERFACE_ID || + interfaceID == CONTENT_INTERFACE_ID || + interfaceID == NAME_INTERFACE_ID || + interfaceID == ABI_INTERFACE_ID || + interfaceID == PUBKEY_INTERFACE_ID || + interfaceID == TEXT_INTERFACE_ID || + interfaceID == MULTIHASH_INTERFACE_ID || + interfaceID == INTERFACE_META_ID; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/SafeMathChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/SafeMathChainlink.sol new file mode 100644 index 0000000..bd1f5b0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/SafeMathChainlink.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.4.11; + + +/** + * @title SafeMath + * @dev Math operations with safety checks that throw on error + */ +library SafeMathChainlink { + + /** + * @dev Multiplies two numbers, throws on overflow. + */ + function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { + // Gas optimization: this is cheaper than asserting 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (_a == 0) { + return 0; + } + + c = _a * _b; + assert(c / _a == _b); + return c; + } + + /** + * @dev Integer division of two numbers, truncating the quotient. + */ + function div(uint256 _a, uint256 _b) internal pure returns (uint256) { + // assert(_b > 0); // Solidity automatically throws when dividing by 0 + // uint256 c = _a / _b; + // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold + return _a / _b; + } + + /** + * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { + assert(_b <= _a); + return _a - _b; + } + + /** + * @dev Adds two numbers, throws on overflow. + */ + function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { + c = _a + _b; + assert(c >= _a); + return c; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/SignedSafeMath.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/SignedSafeMath.sol new file mode 100644 index 0000000..307463a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/SignedSafeMath.sol @@ -0,0 +1,60 @@ +pragma solidity 0.4.24; + +/** + * @title SignedSafeMath + * @dev Signed math operations with safety checks that revert on error. + */ +library SignedSafeMath { + int256 constant private _INT256_MIN = -2**255; + + /** + * @dev Multiplies two signed integers, reverts on overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); + + int256 c = a * b; + require(c / a == b, "SignedSafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + require(b != 0, "SignedSafeMath: division by zero"); + require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); + + int256 c = a / b; + + return c; + } + + /** + * @dev Subtracts two signed integers, reverts on overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + int256 c = a - b; + require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); + + return c; + } + + /** + * @dev Adds two signed integers, reverts on overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + int256 c = a + b; + require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); + + return c; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/StandardToken.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/StandardToken.sol new file mode 100644 index 0000000..7f2b413 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.4/vendor/StandardToken.sol @@ -0,0 +1,85 @@ +pragma solidity ^0.4.11; + + +import { BasicToken as linkBasicToken } from "./BasicToken.sol"; +import { ERC20 as linkERC20 } from "../interfaces/ERC20.sol"; + + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * @dev https://github.com/ethereum/EIPs/issues/20 + * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract StandardToken is linkERC20, linkBasicToken { + + mapping (address => mapping (address => uint256)) allowed; + + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom(address _from, address _to, uint256 _value) returns (bool) { + var _allowance = allowed[_from][msg.sender]; + + // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met + // require (_value <= _allowance); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + allowed[_from][msg.sender] = _allowance.sub(_value); + Transfer(_from, _to, _value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint256 _value) returns (bool) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + return true; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address _owner, address _spender) constant returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + /* + * approve should be called when allowed[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + */ + function increaseApproval (address _spender, uint _addedValue) + returns (bool success) { + allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); + Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + function decreaseApproval (address _spender, uint _subtractedValue) + returns (bool success) { + uint oldValue = allowed[msg.sender][_spender]; + if (_subtractedValue > oldValue) { + allowed[msg.sender][_spender] = 0; + } else { + allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); + } + Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/Chainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Chainlink.sol new file mode 100644 index 0000000..4226048 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Chainlink.sol @@ -0,0 +1,126 @@ +pragma solidity ^0.5.0; + +import { CBOR as CBOR_Chainlink } from "./vendor/CBOR.sol"; +import { Buffer as Buffer_Chainlink } from "./vendor/Buffer.sol"; + +/** + * @title Library for common Chainlink functions + * @dev Uses imported CBOR library for encoding to buffer + */ +library Chainlink { + uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase + + using CBOR_Chainlink for Buffer_Chainlink.buffer; + + struct Request { + bytes32 id; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + Buffer_Chainlink.buffer buf; + } + + /** + * @notice Initializes a Chainlink request + * @dev Sets the ID, callback address, and callback function signature on the request + * @param self The uninitialized request + * @param _id The Job Specification ID + * @param _callbackAddress The callback address + * @param _callbackFunction The callback function signature + * @return The initialized request + */ + function initialize( + Request memory self, + bytes32 _id, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (Chainlink.Request memory) { + Buffer_Chainlink.init(self.buf, defaultBufferSize); + self.id = _id; + self.callbackAddress = _callbackAddress; + self.callbackFunctionId = _callbackFunction; + return self; + } + + /** + * @notice Sets the data for the buffer without encoding CBOR on-chain + * @dev CBOR can be closed with curly-brackets {} or they can be left off + * @param self The initialized request + * @param _data The CBOR data + */ + function setBuffer(Request memory self, bytes memory _data) + internal pure + { + Buffer_Chainlink.init(self.buf, _data.length); + Buffer_Chainlink.append(self.buf, _data); + } + + /** + * @notice Adds a string value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The string value to add + */ + function add(Request memory self, string memory _key, string memory _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeString(_value); + } + + /** + * @notice Adds a bytes value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The bytes value to add + */ + function addBytes(Request memory self, string memory _key, bytes memory _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeBytes(_value); + } + + /** + * @notice Adds a int256 value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The int256 value to add + */ + function addInt(Request memory self, string memory _key, int256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeInt(_value); + } + + /** + * @notice Adds a uint256 value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The uint256 value to add + */ + function addUint(Request memory self, string memory _key, uint256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeUInt(_value); + } + + /** + * @notice Adds an array of strings to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _values The array of string values to add + */ + function addStringArray(Request memory self, string memory _key, string[] memory _values) + internal pure + { + self.buf.encodeString(_key); + self.buf.startArray(); + for (uint256 i = 0; i < _values.length; i++) { + self.buf.encodeString(_values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/ChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/ChainlinkClient.sol new file mode 100644 index 0000000..2c4f794 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/ChainlinkClient.sol @@ -0,0 +1,261 @@ +pragma solidity ^0.5.0; + +import "./Chainlink.sol"; +import "./interfaces/ENSInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/PointerInterface.sol"; +import { ENSResolver as ENSResolver_Chainlink } from "./vendor/ENSResolver.sol"; + +/** + * @title The ChainlinkClient contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network + */ +contract ChainlinkClient { + using Chainlink for Chainlink.Request; + + uint256 constant internal LINK = 10**18; + uint256 constant private AMOUNT_OVERRIDE = 0; + address constant private SENDER_OVERRIDE = address(0); + uint256 constant private ARGS_VERSION = 1; + bytes32 constant private ENS_TOKEN_SUBNAME = keccak256("link"); + bytes32 constant private ENS_ORACLE_SUBNAME = keccak256("oracle"); + address constant private LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; + + ENSInterface private ens; + bytes32 private ensNode; + LinkTokenInterface private link; + ChainlinkRequestInterface private oracle; + uint256 private requestCount = 1; + mapping(bytes32 => address) private pendingRequests; + + event ChainlinkRequested(bytes32 indexed id); + event ChainlinkFulfilled(bytes32 indexed id); + event ChainlinkCancelled(bytes32 indexed id); + + /** + * @notice Creates a request that can hold additional parameters + * @param _specId The Job Specification ID that the request will be created for + * @param _callbackAddress The callback address that the response will be sent to + * @param _callbackFunctionSignature The callback function signature to use for the callback address + * @return A Chainlink Request struct in memory + */ + function buildChainlinkRequest( + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunctionSignature + ) internal pure returns (Chainlink.Request memory) { + Chainlink.Request memory req; + return req.initialize(_specId, _callbackAddress, _callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `chainlinkRequestTo` with the stored oracle address + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return The request ID + */ + function sendChainlinkRequest(Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32) + { + return sendChainlinkRequestTo(address(oracle), _req, _payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param _oracle The address of the oracle for the request + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return The request ID + */ + function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, requestCount)); + _req.nonce = requestCount; + pendingRequests[requestId] = _oracle; + emit ChainlinkRequested(requestId); + require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle"); + requestCount += 1; + + return requestId; + } + + /** + * @notice Allows a request to be cancelled if it has not been fulfilled + * @dev Requires keeping track of the expiration value emitted from the oracle contract. + * Deletes the request from the `pendingRequests` mapping. + * Emits ChainlinkCancelled event. + * @param _requestId The request ID + * @param _payment The amount of LINK sent for the request + * @param _callbackFunc The callback function specified for the request + * @param _expiration The time of the expiration for the request + */ + function cancelChainlinkRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunc, + uint256 _expiration + ) + internal + { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(pendingRequests[_requestId]); + delete pendingRequests[_requestId]; + emit ChainlinkCancelled(_requestId); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunc, _expiration); + } + + /** + * @notice Sets the stored oracle address + * @param _oracle The address of the oracle contract + */ + function setChainlinkOracle(address _oracle) internal { + oracle = ChainlinkRequestInterface(_oracle); + } + + /** + * @notice Sets the LINK token address + * @param _link The address of the LINK token contract + */ + function setChainlinkToken(address _link) internal { + link = LinkTokenInterface(_link); + } + + /** + * @notice Sets the Chainlink token address for the public + * network as given by the Pointer contract + */ + function setPublicChainlinkToken() internal { + setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkTokenAddress() + internal + view + returns (address) + { + return address(link); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function chainlinkOracleAddress() + internal + view + returns (address) + { + return address(oracle); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param _oracle The address of the oracle contract that will fulfill the request + * @param _requestId The request ID used for the response + */ + function addChainlinkExternalRequest(address _oracle, bytes32 _requestId) + internal + notPendingRequest(_requestId) + { + pendingRequests[_requestId] = _oracle; + } + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param _ens The address of the ENS contract + * @param _node The ENS node hash + */ + function useChainlinkWithENS(address _ens, bytes32 _node) + internal + { + ens = ENSInterface(_ens); + ensNode = _node; + bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ENS_TOKEN_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(linkSubnode)); + setChainlinkToken(resolver.addr(linkSubnode)); + updateChainlinkOracleWithENS(); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously + */ + function updateChainlinkOracleWithENS() + internal + { + bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(oracleSubnode)); + setChainlinkOracle(resolver.addr(oracleSubnode)); + } + + /** + * @notice Encodes the request to be sent to the oracle contract + * @dev The Chainlink node expects values to be in order for the request to be picked up. Order of types + * will be validated in the oracle contract. + * @param _req The initialized Chainlink Request + * @return The bytes payload for the `transferAndCall` method + */ + function encodeRequest(Chainlink.Request memory _req) + private + view + returns (bytes memory) + { + return abi.encodeWithSelector( + oracle.oracleRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + ARGS_VERSION, + _req.buf.buf); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param _requestId The request ID for fulfillment + */ + function validateChainlinkCallback(bytes32 _requestId) + internal + recordChainlinkFulfillment(_requestId) + // solhint-disable-next-line no-empty-blocks + {} + + /** + * @dev Reverts if the sender is not the oracle of the request. + * Emits ChainlinkFulfilled event. + * @param _requestId The request ID for fulfillment + */ + modifier recordChainlinkFulfillment(bytes32 _requestId) { + require(msg.sender == pendingRequests[_requestId], + "Source must be the oracle of the request"); + delete pendingRequests[_requestId]; + emit ChainlinkFulfilled(_requestId); + _; + } + + /** + * @dev Reverts if the request is already pending + * @param _requestId The request ID for fulfillment + */ + modifier notPendingRequest(bytes32 _requestId) { + require(pendingRequests[_requestId] == address(0), "Request is already pending"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/LinkTokenReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/LinkTokenReceiver.sol new file mode 100644 index 0000000..6d4cb92 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/LinkTokenReceiver.sol @@ -0,0 +1,70 @@ +pragma solidity ^0.5.0; + +contract LinkTokenReceiver { + + bytes4 constant private ORACLE_REQUEST_SELECTOR = 0x40429946; + uint256 constant private SELECTOR_LENGTH = 4; + uint256 constant private EXPECTED_REQUEST_WORDS = 2; + uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` + * values to ensure correctness. Calls oracleRequest. + * @param _sender Address of the sender + * @param _amount Amount of LINK sent (specified in wei) + * @param _data Payload of the transaction + */ + function onTokenTransfer( + address _sender, + uint256 _amount, + bytes memory _data + ) + public + onlyLINK + validRequestLength(_data) + permittedFunctionsForLINK(_data) + { + assembly { + // solhint-disable-next-line avoid-low-level-calls + mstore(add(_data, 36), _sender) // ensure correct sender is passed + // solhint-disable-next-line avoid-low-level-calls + 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 + require(success, "Unable to create request"); + } + + function getChainlinkToken() public view returns (address); + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + require(msg.sender == getChainlinkToken(), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `oracleRequest` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory _data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(_data, 32)) + } + require(funcSelector == ORACLE_REQUEST_SELECTOR, "Must use whitelisted functions"); + _; + } + + /** + * @dev Reverts if the given payload is less than needed to create a request + * @param _data The request payload + */ + modifier validRequestLength(bytes memory _data) { + require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); + _; + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/Median.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Median.sol new file mode 100644 index 0000000..5f75337 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Median.sol @@ -0,0 +1,108 @@ +pragma solidity ^0.5.0; + +import "./vendor/SafeMathChainlink.sol"; +import "./vendor/SignedSafeMath.sol"; + +library Median { + using SafeMathChainlink for uint256; + using SignedSafeMath for int256; + + /** + * @dev Returns the sorted middle, or the average of the two middle indexed + * items if the array has an even number of elements + * @param _list The list of elements to compare + */ + function calculate(int256[] memory _list) + internal + pure + returns (int256) + { + uint256 answerLength = _list.length; + uint256 middleIndex = answerLength.div(2); + if (answerLength % 2 == 0) { + int256 median1 = quickselect(copy(_list), middleIndex); + int256 median2 = quickselect(_list, middleIndex.add(1)); // quickselect is 1 indexed + int256 remainder = (median1 % 2 + median2 % 2) / 2; + return (median1 / 2).add(median2 / 2).add(remainder); // signed integers are not supported by SafeMath + } else { + return quickselect(_list, middleIndex.add(1)); // quickselect is 1 indexed + } + } + + /** + * @dev Returns the kth value of the ordered array + * See: http://www.cs.yale.edu/homes/aspnes/pinewiki/QuickSelect.html + * @param _a The list of elements to pull from + * @param _k The index, 1 based, of the elements you want to pull from when ordered + */ + function quickselect(int256[] memory _a, uint256 _k) + private + pure + returns (int256) + { + int256[] memory a = _a; + uint256 k = _k; + uint256 aLen = a.length; + int256[] memory a1 = new int256[](aLen); + int256[] memory a2 = new int256[](aLen); + uint256 a1Len; + uint256 a2Len; + int256 pivot; + uint256 i; + + while (true) { + pivot = a[aLen.div(2)]; + a1Len = 0; + a2Len = 0; + for (i = 0; i < aLen; i++) { + if (a[i] < pivot) { + a1[a1Len] = a[i]; + a1Len++; + } else if (a[i] > pivot) { + a2[a2Len] = a[i]; + a2Len++; + } + } + if (k <= a1Len) { + aLen = a1Len; + (a, a1) = swap(a, a1); + } else if (k > (aLen.sub(a2Len))) { + k = k.sub(aLen.sub(a2Len)); + aLen = a2Len; + (a, a2) = swap(a, a2); + } else { + return pivot; + } + } + } + + /** + * @dev Swaps the pointers to two uint256 arrays in memory + * @param _a The pointer to the first in memory array + * @param _b The pointer to the second in memory array + */ + function swap(int256[] memory _a, int256[] memory _b) + private + pure + returns(int256[] memory, int256[] memory) + { + return (_b, _a); + } + + /** + * @dev Makes an in memory copy of the array passed in + * @param _list The pointer to the array to be copied + */ + function copy(int256[] memory _list) + private + pure + returns(int256[] memory) + { + int256[] memory list2 = new int256[](_list.length); + for (uint256 i = 0; i < _list.length; i++) { + list2[i] = _list[i]; + } + return list2; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/Migrations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Migrations.sol new file mode 100644 index 0000000..d62541c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.5.0; // solhint-disable-line compiler-fixed + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + constructor() public { + owner = msg.sender; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/Oracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Oracle.sol new file mode 100644 index 0000000..186695d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/Oracle.sol @@ -0,0 +1,273 @@ +pragma solidity ^0.5.0; + +import "./LinkTokenReceiver.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/OracleInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/WithdrawalInterface.sol"; +import "./vendor/Ownable.sol"; +import "./vendor/SafeMathChainlink.sol"; + +/** + * @title The Chainlink Oracle contract + * @notice Node operators can deploy this contract to fulfill requests sent to them + */ +contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable, LinkTokenReceiver, WithdrawalInterface { + using SafeMathChainlink for uint256; + + uint256 constant public EXPIRY_TIME = 5 minutes; + uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; + // We initialize fields to 1 instead of 0 so that the first invocation + // does not cost more gas. + uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; + + LinkTokenInterface internal LinkToken; + mapping(bytes32 => bytes32) private commitments; + mapping(address => bool) private authorizedNodes; + uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; + + 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 Ownable() { + 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 + onlyLINK + checkCallbackAddress(_callbackAddress) + { + bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); + require(commitments[requestId] == 0, "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + uint256 expiration = now.add(EXPIRY_TIME); + + commitments[requestId] = keccak256( + abi.encodePacked( + _payment, + _callbackAddress, + _callbackFunctionId, + expiration + ) + ); + + 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 _payment The payment amount that will be released for the oracle (specified in wei) + * @param _callbackAddress The callback address to call for fulfillment + * @param _callbackFunctionId The callback function ID to use for fulfillment + * @param _expiration The expiration that the node should respond by before the requester can cancel + * @param _data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest( + bytes32 _requestId, + uint256 _payment, + address _callbackAddress, + bytes4 _callbackFunctionId, + uint256 _expiration, + bytes32 _data + ) + external + onlyAuthorizedNode + isValidRequest(_requestId) + returns (bool) + { + bytes32 paramsHash = keccak256( + abi.encodePacked( + _payment, + _callbackAddress, + _callbackFunctionId, + _expiration + ) + ); + require(commitments[_requestId] == paramsHash, "Params do not match request ID"); + withdrawableTokens = withdrawableTokens.add(_payment); + 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, ) = _callbackAddress.call(abi.encodeWithSelector(_callbackFunctionId, _requestId, _data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Use this to check if a node is authorized for fulfilling requests + * @param _node The address of the Chainlink node + * @return The authorization status of the node + */ + function getAuthorizationStatus(address _node) external view returns (bool) { + return authorizedNodes[_node]; + } + + /** + * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. + * @param _node The address of the Chainlink node + * @param _allowed Bool value to determine if the node can fulfill requests + */ + function setFulfillmentPermission(address _node, bool _allowed) external onlyOwner { + authorizedNodes[_node] = _allowed; + } + + /** + * @notice Allows the node operator to withdraw earned LINK to a given address + * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node + * @param _recipient The address to send the LINK token to + * @param _amount The amount to send (specified in wei) + */ + function withdraw(address _recipient, uint256 _amount) + external + onlyOwner + hasAvailableFunds(_amount) + { + withdrawableTokens = withdrawableTokens.sub(_amount); + assert(LinkToken.transfer(_recipient, _amount)); + } + + /** + * @notice Displays the amount of LINK that is available for the node operator to withdraw + * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage + * @return The amount of withdrawable LINK on the contract + */ + function withdrawable() external view onlyOwner returns (uint256) { + return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); + } + + /** + * @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 _callbackFunc The requester's specified callback address + * @param _expiration The time of the expiration for the request + */ + function cancelOracleRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunc, + uint256 _expiration + ) external { + bytes32 paramsHash = keccak256( + abi.encodePacked( + _payment, + msg.sender, + _callbackFunc, + _expiration) + ); + require(paramsHash == commitments[_requestId], "Params do not match request 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 returns (address) { + return address(LinkToken); + } + + // MODIFIERS + + /** + * @dev Reverts if amount requested is greater than withdrawable balance + * @param _amount The given amount to compare to `withdrawableTokens` + */ + modifier hasAvailableFunds(uint256 _amount) { + require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); + _; + } + + /** + * @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] != 0, "Must have a valid requestId"); + _; + } + + /** + * @dev Reverts if `msg.sender` is not authorized to fulfill requests + */ + modifier onlyAuthorizedNode() { + require(authorizedNodes[msg.sender] || msg.sender == owner(), "Not an authorized node to fulfill requests"); + _; + } + + /** + * @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/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/Coordinator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/Coordinator.sol new file mode 100644 index 0000000..23bdfb6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/Coordinator.sol @@ -0,0 +1,411 @@ +pragma solidity 0.5.0; + +import "./CoordinatorInterface.sol"; +import "../interfaces/ChainlinkRequestInterface.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../vendor/SafeMathChainlink.sol"; +import "./ServiceAgreementDecoder.sol"; +import "./OracleSignaturesDecoder.sol"; + + +/** + * @title The Chainlink Coordinator handles oracle service agreements between one or more oracles + */ +contract Coordinator is ChainlinkRequestInterface, CoordinatorInterface, ServiceAgreementDecoder, OracleSignaturesDecoder { + using SafeMathChainlink for uint256; + + uint256 constant public EXPIRY_TIME = 5 minutes; + LinkTokenInterface internal LINK; + + struct Callback { + bytes32 sAId; + uint256 amount; + address addr; + bytes4 functionId; + uint64 cancelExpiration; + uint8 responseCount; + mapping(address => uint256) responses; + } + + mapping(bytes32 => Callback) private callbacks; + mapping(bytes32 => mapping(address => bool)) private allowedOracles; + mapping(bytes32 => ServiceAgreement) public serviceAgreements; + mapping(address => uint256) public withdrawableTokens; + + /** + * @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 { + LINK = LinkTokenInterface(_link); + } + + event OracleRequest( + bytes32 indexed sAId, + address requester, + bytes32 requestId, + uint256 payment, + address callbackAddr, + bytes4 callbackFunctionId, + uint256 cancelExpiration, + uint256 dataVersion, + bytes data + ); + + event NewServiceAgreement( + bytes32 indexed said, + bytes32 indexed requestDigest + ); + + event CancelOracleRequest( + bytes32 internalId + ); + + /** + * @notice Creates the Chainlink request + * @dev Stores the params on-chain in a callback for the request. + * Emits OracleRequest event for Chainlink nodes to detect. + * @param _sender The sender of the request + * @param _amount The amount of payment given (specified in wei) + * @param _sAId The Service Agreement 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 _amount, + bytes32 _sAId, + address _callbackAddress, + bytes4 _callbackFunctionId, + uint256 _nonce, + uint256 _dataVersion, + bytes calldata _data + ) + external + onlyLINK + sufficientLINK(_amount, _sAId) + checkCallbackAddress(_callbackAddress) + // checkServiceAgreementPresence(_sAId) // TODO: exhausts the stack + { + bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce)); + require(callbacks[requestId].cancelExpiration == 0, "Must use a unique ID"); + callbacks[requestId].sAId = _sAId; + callbacks[requestId].amount = _amount; + callbacks[requestId].addr = _callbackAddress; + callbacks[requestId].functionId = _callbackFunctionId; + // solhint-disable-next-line not-rely-on-time + callbacks[requestId].cancelExpiration = uint64(now.add(EXPIRY_TIME)); + + emit OracleRequest( + _sAId, + _sender, + requestId, + _amount, + _callbackAddress, + _callbackFunctionId, + now.add(EXPIRY_TIME), // solhint-disable-line not-rely-on-time + _dataVersion, + _data); + } + + /** + * @notice Stores a Service Agreement which has been signed by the given oracles + * @dev Validates that each oracle has a valid signature. + * Emits NewServiceAgreement event. + * @return The Service Agreement ID + */ + function initiateServiceAgreement( + bytes memory _serviceAgreementData, + bytes memory _oracleSignaturesData + ) + public + returns (bytes32 serviceAgreementID) + { + + ServiceAgreement memory _agreement = decodeServiceAgreement(_serviceAgreementData); + OracleSignatures memory _signatures = decodeOracleSignatures(_oracleSignaturesData); + + require( + _agreement.oracles.length == _signatures.vs.length && + _signatures.vs.length == _signatures.rs.length && + _signatures.rs.length == _signatures.ss.length, + "Must pass in as many signatures as oracles" + ); + // solhint-disable-next-line not-rely-on-time + require(_agreement.endAt > block.timestamp, + "ServiceAgreement must end in the future"); + require(serviceAgreements[serviceAgreementID].endAt == 0, + "serviceAgreement already initiated"); + serviceAgreementID = getId(_agreement); + + registerOracleSignatures( + serviceAgreementID, + _agreement.oracles, + _signatures + ); + + serviceAgreements[serviceAgreementID] = _agreement; + emit NewServiceAgreement(serviceAgreementID, _agreement.requestDigest); + // solhint-disable-next-line avoid-low-level-calls + (bool ok, bytes memory response) = _agreement.aggregator.call( + abi.encodeWithSelector( + _agreement.aggInitiateJobSelector, + serviceAgreementID, + _serviceAgreementData + ) + ); + require(ok, "Aggregator failed to initiate Service Agreement"); + require(response.length > 0, "probably wrong address/selector"); + (bool success, bytes memory message) = abi.decode(response, (bool, bytes)); + if ((!success) && message.length == 0) { + // Revert with a non-empty message to give user a hint where to look + require(success, "initiation failed; empty message"); + } + require(success, string(message)); + } + + /** + * @dev Validates that each signer address matches for the given oracles + * @param _serviceAgreementID Service agreement ID + * @param _oracles Array of oracle addresses which agreed to the service agreement + * @param _signatures contains the collected parts(v, r, and s) of each oracle's signature. + */ + function registerOracleSignatures( + bytes32 _serviceAgreementID, + address[] memory _oracles, + OracleSignatures memory _signatures + ) + private + { + for (uint i = 0; i < _oracles.length; i++) { + address signer = getOracleAddressFromSASignature( + _serviceAgreementID, + _signatures.vs[i], + _signatures.rs[i], + _signatures.ss[i] + ); + require(_oracles[i] == signer, "Invalid oracle signature specified in SA"); + allowedOracles[_serviceAgreementID][_oracles[i]] = true; + } + + } + + /** + * @dev Recovers the address of the signer for a service agreement + * @param _serviceAgreementID Service agreement ID + * @param _v Recovery ID of the oracle signature + * @param _r First 32 bytes of the oracle signature + * @param _s Second 32 bytes of the oracle signature + * @return The address of the signer + */ + function getOracleAddressFromSASignature( + bytes32 _serviceAgreementID, + uint8 _v, + bytes32 _r, + bytes32 _s + ) + private pure returns (address) + { + bytes32 prefixedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _serviceAgreementID)); + return ecrecover(prefixedHash, _v, _r, _s); + } + + /** + * @notice Called by the Chainlink node to fulfill requests + * @dev Response must have a valid callback, and will delete the associated callback storage + * before calling the external contract. + * @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) { + Callback memory callback = callbacks[_requestId]; + ServiceAgreement memory sA = serviceAgreements[callback.sAId]; + // solhint-disable-next-line avoid-low-level-calls + (bool ok, bytes memory aggResponse) = sA.aggregator.call( + abi.encodeWithSelector( + sA.aggFulfillSelector, _requestId, callback.sAId, msg.sender, _data)); + require(ok, "aggregator.fulfill failed"); + require(aggResponse.length > 0, "probably wrong address/selector"); + (bool aggSuccess, bool aggComplete, bytes memory response, int256[] memory paymentAmounts) = abi.decode( // solhint-disable-line + aggResponse, (bool, bool, bytes, int256[])); + require(aggSuccess, string(response)); + if (aggComplete) { + require(paymentAmounts.length == sA.oracles.length, "wrong paymentAmounts.length"); + for (uint256 oIdx = 0; oIdx < sA.oracles.length; oIdx++) { // pay oracles + withdrawableTokens[sA.oracles[oIdx]] = uint256(int256( + withdrawableTokens[sA.oracles[oIdx]]) + paymentAmounts[oIdx]); + } // solhint-disable-next-line avoid-low-level-calls + (bool success,) = callback.addr.call(abi.encodeWithSelector( // report final result + callback.functionId, _requestId, abi.decode(response, (bytes32)))); + return success; + } + return true; + } + + /** + * @dev Allows the oracle operator to withdraw their LINK + * @param _recipient is the address the funds will be sent to + * @param _amount is the amount of LINK transferred from the Coordinator contract + */ + function withdraw(address _recipient, uint256 _amount) + external + hasAvailableFunds(_amount) + { + withdrawableTokens[msg.sender] = withdrawableTokens[msg.sender].sub(_amount); + assert(LINK.transfer(_recipient, _amount)); + } + + /** + * @dev Necessary to implement ChainlinkRequestInterface + */ + function cancelOracleRequest(bytes32, uint256, bytes4, uint256) + external + {} // solhint-disable-line no-empty-blocks + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` + * values to ensure correctness. Calls oracleRequest. + * @param _sender Address of the sender + * @param _amount Amount of LINK sent (specified in wei) + * @param _data Payload of the transaction + */ + function onTokenTransfer( + address _sender, + uint256 _amount, + bytes memory _data + ) + public + onlyLINK + permittedFunctionsForLINK + { + assembly { // solhint-disable-line no-inline-assembly + mstore(add(_data, 36), _sender) // ensure correct sender is passed + 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 or depositFunds + require(success, "Unable to create request"); + } + + /** + * @notice Retrieve the Service Agreement ID for the given parameters + * @param _agreementData contains all of the terms of the service agreement that can be verified on-chain. + * @return The Service Agreement ID, a keccak256 hash of the input params + */ + function getId(bytes memory _agreementData) public pure returns (bytes32) + { + ServiceAgreement memory _agreement = decodeServiceAgreement(_agreementData); + return getId(_agreement); + } + + function getId(ServiceAgreement memory _agreement) internal pure returns (bytes32) + { + return keccak256( + abi.encodePacked( + _agreement.payment, + _agreement.expiration, + _agreement.endAt, + _agreement.oracles, + _agreement.requestDigest, + _agreement.aggregator, + _agreement.aggInitiateJobSelector, + _agreement.aggFulfillSelector + )); + } + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @param _sender Address of the sender + * @param _amount Amount of LINK sent (specified in wei) + */ + function depositFunds(address _sender, uint256 _amount) external onlyLINK + { + withdrawableTokens[_sender] = withdrawableTokens[_sender].add(_amount); + } + + /** + * @param _account Address to check balance of + * @return Balance of account (specified in wei) + */ + function balanceOf(address _account) public view returns (uint256) + { + return withdrawableTokens[_account]; + } + + /** + * @dev Reverts if the callback address is the LINK token + * @param _to The callback address + */ + modifier checkCallbackAddress(address _to) { + require(_to != address(LINK), "Cannot callback to LINK"); + _; + } + + /** + * @dev Reverts if amount requested is greater than withdrawable balance + * @param _amount The given amount to compare to `withdrawableTokens` + */ + modifier hasAvailableFunds(uint256 _amount) { + require(withdrawableTokens[msg.sender] >= _amount, "Amount requested is greater than withdrawable balance"); + _; + } + + /** + * @dev Reverts if request ID does not exist + * @param _requestId The given request ID to check in stored `callbacks` + */ + modifier isValidRequest(bytes32 _requestId) { + require(callbacks[_requestId].addr != address(0), "Must have a valid requestId"); + require(allowedOracles[callbacks[_requestId].sAId][msg.sender], "Oracle not recognized on service agreement"); + _; + } + + /** + * @dev Reverts if amount is not at least what was agreed upon in the service agreement + * @param _amount The payment for the request + * @param _sAId The service agreement ID which the request is for + */ + modifier sufficientLINK(uint256 _amount, bytes32 _sAId) { + require(_amount >= serviceAgreements[_sAId].payment, "Below agreed payment"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `oracleRequest` or + * `depositFunds` function selector + */ + modifier permittedFunctionsForLINK() { + bytes4[1] memory funcSelector; + assembly { // solhint-disable-line no-inline-assembly + calldatacopy(funcSelector, 132, 4) // grab function selector from calldata + } + require( + funcSelector[0] == this.oracleRequest.selector || funcSelector[0] == this.depositFunds.selector, + "Must use whitelisted functions" + ); + _; + } + + modifier checkServiceAgreementPresence(bytes32 _sAId) { + require(uint256(serviceAgreements[_sAId].requestDigest) != 0, + "Must reference an existing ServiceAgreement"); + _; + } + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + require(msg.sender == address(LINK), "Must use LINK token"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/CoordinatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/CoordinatorInterface.sol new file mode 100644 index 0000000..1678a95 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/CoordinatorInterface.sol @@ -0,0 +1,14 @@ +pragma solidity 0.5.0; + +contract CoordinatorInterface { + + function initiateServiceAgreement( + bytes memory _serviceAgreementData, + bytes memory _oracleSignaturesData) + public returns (bytes32); + + function fulfillOracleRequest( + bytes32 _requestId, + bytes32 _aggregatorArgs) + external returns (bool); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/OracleSignaturesDecoder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/OracleSignaturesDecoder.sol new file mode 100644 index 0000000..1c2776b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/OracleSignaturesDecoder.sol @@ -0,0 +1,24 @@ +pragma solidity 0.5.0; + +contract OracleSignaturesDecoder { + + struct OracleSignatures { + uint8[] vs; + bytes32[] rs; + bytes32[] ss; + } + + function decodeOracleSignatures( + bytes memory _oracleSignaturesData + ) + internal + pure + returns(OracleSignatures memory) + { + // solhint-disable indent + OracleSignatures memory signatures; + ( signatures.vs, signatures.rs, signatures.ss) = + abi.decode(_oracleSignaturesData, ( uint8[], bytes32[], bytes32[] )); + return signatures; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/SchnorrSECP256K1.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/SchnorrSECP256K1.sol new file mode 100644 index 0000000..192c884 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/SchnorrSECP256K1.sol @@ -0,0 +1,147 @@ +pragma solidity ^0.5.0; + +//////////////////////////////////////////////////////////////////////////////// +// XXX: Do not use in production until this code has been audited. +//////////////////////////////////////////////////////////////////////////////// + +contract SchnorrSECP256K1 { + // See https://en.bitcoin.it/wiki/Secp256k1 for this constant. + uint256 constant public Q = // Group order of secp256k1 + // solium-disable-next-line indentation + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + // solium-disable-next-line zeppelin/no-arithmetic-operations + uint256 constant public HALF_Q = (Q >> 1) + 1; + + /** ************************************************************************** + @notice verifySignature returns true iff passed a valid Schnorr signature. + + @dev See https://en.wikipedia.org/wiki/Schnorr_signature for reference. + + @dev In what follows, let d be your secret key, PK be your public key, + PKx be the x ordinate of your public key, and PKyp be the parity bit for + the y ordinate (i.e., 0 if PKy is even, 1 if odd.) + ************************************************************************** + @dev TO CREATE A VALID SIGNATURE FOR THIS METHOD + + @dev First PKx must be less than HALF_Q. Then follow these instructions + (see evm/test/schnorr_test.js, for an example of carrying them out): + @dev 1. Hash the target message to a uint256, called msgHash here, using + keccak256 + + @dev 2. Pick k uniformly and cryptographically securely randomly from + {0,...,Q-1}. It is critical that k remains confidential, as your + private key can be reconstructed from k and the signature. + + @dev 3. Compute k*g in the secp256k1 group, where g is the group + generator. (This is the same as computing the public key from the + secret key k. But it's OK if k*g's x ordinate is greater than + HALF_Q.) + + @dev 4. Compute the ethereum address for k*g. This is the lower 160 bits + of the keccak hash of the concatenated affine coordinates of k*g, + as 32-byte big-endians. (For instance, you could pass k to + ethereumjs-utils's privateToAddress to compute this, though that + should be strictly a development convenience, not for handling + live secrets, unless you've locked your javascript environment + down very carefully.) Call this address + nonceTimesGeneratorAddress. + + @dev 5. Compute e=uint256(keccak256(PKx as a 32-byte big-endian + ‖ PKyp as a single byte + ‖ msgHash + ‖ nonceTimesGeneratorAddress)) + This value e is called "msgChallenge" in verifySignature's source + code below. Here "‖" means concatenation of the listed byte + arrays. + + @dev 6. Let x be your secret key. Compute s = (k - d * e) % Q. Add Q to + it, if it's negative. This is your signature. (d is your secret + key.) + ************************************************************************** + @dev TO VERIFY A SIGNATURE + + @dev Given a signature (s, e) of msgHash, constructed as above, compute + S=e*PK+s*generator in the secp256k1 group law, and then the ethereum + address of S, as described in step 4. Call that + nonceTimesGeneratorAddress. Then call the verifySignature method as: + + @dev verifySignature(PKx, PKyp, s, msgHash, + nonceTimesGeneratorAddress) + ************************************************************************** + @dev This signging scheme deviates slightly from the classical Schnorr + signature, in that the address of k*g is used in place of k*g itself, + both when calculating e and when verifying sum S as described in the + verification paragraph above. This reduces the difficulty of + brute-forcing a signature by trying random secp256k1 points in place of + k*g in the signature verification process from 256 bits to 160 bits. + However, the difficulty of cracking the public key using "baby-step, + giant-step" is only 128 bits, so this weakening constitutes no compromise + in the security of the signatures or the key. + + @dev The constraint signingPubKeyX < HALF_Q comes from Eq. (281), p. 24 + of Yellow Paper version 78d7b9a. ecrecover only accepts "s" inputs less + than HALF_Q, to protect against a signature- malleability vulnerability in + ECDSA. Schnorr does not have this vulnerability, but we must account for + ecrecover's defense anyway. And since we are abusing ecrecover by putting + signingPubKeyX in ecrecover's "s" argument the constraint applies to + signingPubKeyX, even though it represents a value in the base field, and + has no natural relationship to the order of the curve's cyclic group. + ************************************************************************** + @param signingPubKeyX is the x ordinate of the public key. This must be + less than HALF_Q. + @param pubKeyYParity is 0 if the y ordinate of the public key is even, 1 + if it's odd. + @param signature is the actual signature, described as s in the above + instructions. + @param msgHash is a 256-bit hash of the message being signed. + @param nonceTimesGeneratorAddress is the ethereum address of k*g in the + above instructions + ************************************************************************** + @return True if passed a valid signature, false otherwise. */ + function verifySignature( + uint256 signingPubKeyX, + uint8 pubKeyYParity, + uint256 signature, + uint256 msgHash, + address nonceTimesGeneratorAddress) external pure returns (bool) { + require(signingPubKeyX < HALF_Q, "Public-key x >= HALF_Q"); + // Avoid signature malleability from multiple representations for ℤ/Qℤ elts + require(signature < Q, "signature must be reduced modulo Q"); + + // Forbid trivial inputs, to avoid ecrecover edge cases. The main thing to + // avoid is something which causes ecrecover to return 0x0: then trivial + // signatures could be constructed with the nonceTimesGeneratorAddress input + // set to 0x0. + // + // solium-disable-next-line indentation + require(nonceTimesGeneratorAddress != address(0) && signingPubKeyX > 0 && + signature > 0 && msgHash > 0, "no zero inputs allowed"); + + // solium-disable-next-line indentation + uint256 msgChallenge = // "e" + // solium-disable-next-line indentation + uint256(keccak256(abi.encodePacked(signingPubKeyX, pubKeyYParity, + msgHash, nonceTimesGeneratorAddress)) + ); + + // Verify msgChallenge * signingPubKey + signature * generator == + // nonce * generator + // + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // The point corresponding to the address returned by + // ecrecover(-s*r,v,r,e*r) is (r⁻¹ mod Q)*(e*r*R-(-s)*r*g)=e*R+s*g, where R + // is the (v,r) point. See https://crypto.stackexchange.com/a/18106 + // + // solium-disable-next-line indentation + address recoveredAddress = ecrecover( + // solium-disable-next-line zeppelin/no-arithmetic-operations + bytes32(Q - mulmod(signingPubKeyX, signature, Q)), + // https://ethereum.github.io/yellowpaper/paper.pdf p. 24, "The + // value 27 represents an even y value and 28 represents an odd + // y value." + (pubKeyYParity == 0) ? 27 : 28, + bytes32(signingPubKeyX), + bytes32(mulmod(msgChallenge, signingPubKeyX, Q))); + return nonceTimesGeneratorAddress == recoveredAddress; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/ServiceAgreementDecoder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/ServiceAgreementDecoder.sol new file mode 100644 index 0000000..9267b77 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/dev/ServiceAgreementDecoder.sol @@ -0,0 +1,59 @@ +pragma solidity 0.5.0; + +contract ServiceAgreementDecoder { + + struct ServiceAgreement { + uint256 payment; + uint256 expiration; + uint256 endAt; + address[] oracles; + // This effectively functions as an ID tag for the off-chain job of the + // service agreement. It is calculated as the keccak256 hash of the + // normalized JSON request to create the ServiceAgreement, but that identity + // is unused, and its value is essentially arbitrary. + bytes32 requestDigest; + // Specification of aggregator interface. See ../tests/MeanAggregator.sol + // for example + address aggregator; + // Selectors for the interface methods must be specified, because their + // arguments can vary from aggregator to aggregator. + // + // Function selector for aggregator initiateJob method + bytes4 aggInitiateJobSelector; + // Function selector for aggregator fulfill method + bytes4 aggFulfillSelector; + } + + function decodeServiceAgreement( + bytes memory _serviceAgreementData + ) + internal + pure + returns(ServiceAgreement memory) + { + // solhint-disable indent + ServiceAgreement memory agreement; + + ( agreement.payment, + agreement.expiration, + agreement.endAt, + agreement.oracles, + agreement.requestDigest, + agreement.aggregator, + agreement.aggInitiateJobSelector, + agreement.aggFulfillSelector) = + abi.decode( + _serviceAgreementData, + ( uint256, + uint256, + uint256, + address[], + bytes32, + address, + bytes4, + bytes4 ) + ); + + return agreement; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorInterface.sol new file mode 100644 index 0000000..d9bd107 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorInterface.sol @@ -0,0 +1,12 @@ +pragma solidity >=0.5.0; + +interface AggregatorInterface { + function latestAnswer() external view returns (int256); + function latestTimestamp() external view returns (uint256); + function latestRound() external view returns (uint256); + function getAnswer(uint256 roundId) external view returns (int256); + function getTimestamp(uint256 roundId) external view returns (uint256); + + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); + event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorV2V3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorV2V3Interface.sol new file mode 100644 index 0000000..0024711 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorV2V3Interface.sol @@ -0,0 +1,56 @@ +pragma solidity >=0.5.0; + +import "./AggregatorInterface.sol"; +import "./AggregatorV3Interface.sol"; + +/** + * @title The V2 & V3 Aggregator Interface + * @notice Solidity V0.5 does not allow interfaces to inherit from other + * interfaces so this contract is a combination of v0.5 AggregatorInterface.sol + * and v0.5 AggregatorV3Interface.sol. + */ +interface AggregatorV2V3Interface { + // + // V2 Interface: + // + function latestAnswer() external view returns (int256); + function latestTimestamp() external view returns (uint256); + function latestRound() external view returns (uint256); + function getAnswer(uint256 roundId) external view returns (int256); + function getTimestamp(uint256 roundId) external view returns (uint256); + + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); + event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); + + // + // V3 Interface: + // + function decimals() external view returns (uint8); + function description() external view returns (string memory); + function version() external view returns (uint256); + + // getRoundData and latestRoundData should both raise "No data present" + // if they do not have data to report, instead of returning unset values + // which could be misinterpreted as actual reported values. + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorV3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorV3Interface.sol new file mode 100644 index 0000000..af8f83d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/AggregatorV3Interface.sol @@ -0,0 +1,33 @@ +pragma solidity >=0.5.0; + +interface AggregatorV3Interface { + + function decimals() external view returns (uint8); + function description() external view returns (string memory); + function version() external view returns (uint256); + + // getRoundData and latestRoundData should both raise "No data present" + // if they do not have data to report, instead of returning unset values + // which could be misinterpreted as actual reported values. + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/ChainlinkRequestInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/ChainlinkRequestInterface.sol new file mode 100644 index 0000000..b58705b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/ChainlinkRequestInterface.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.5.0; + +interface ChainlinkRequestInterface { + function oracleRequest( + address sender, + uint256 requestPrice, + bytes32 serviceAgreementID, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external; + + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunctionId, + uint256 expiration + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/ENSInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/ENSInterface.sol new file mode 100644 index 0000000..1aee391 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/ENSInterface.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.5.0; + +interface ENSInterface { + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + + function setSubnodeOwner(bytes32 node, bytes32 label, address _owner) external; + function setResolver(bytes32 node, address _resolver) external; + function setOwner(bytes32 node, address _owner) external; + function setTTL(bytes32 node, uint64 _ttl) external; + function owner(bytes32 node) external view returns (address); + function resolver(bytes32 node) external view returns (address); + function ttl(bytes32 node) external view returns (uint64); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/FlagsInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/FlagsInterface.sol new file mode 100644 index 0000000..8820329 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/FlagsInterface.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.5.0; + +interface FlagsInterface { + function getFlag(address) external view returns (bool); + function getFlags(address[] calldata) external view returns (bool[] memory); + function raiseFlag(address) external; + function raiseFlags(address[] calldata) external; + function lowerFlags(address[] calldata) external; + function setRaisingAccessController(address) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/LinkTokenInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/LinkTokenInterface.sol new file mode 100644 index 0000000..6865956 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/LinkTokenInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.5.0; + +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); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/OracleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/OracleInterface.sol new file mode 100644 index 0000000..b6847e5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/OracleInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.5.0; + +interface OracleInterface { + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) external returns (bool); + function getAuthorizationStatus(address node) external view returns (bool); + function setFulfillmentPermission(address node, bool allowed) external; + function withdraw(address recipient, uint256 amount) external; + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/PointerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/PointerInterface.sol new file mode 100644 index 0000000..2f3013c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/PointerInterface.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.5.0; + +interface PointerInterface { + function getAddress() external view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/WithdrawalInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/WithdrawalInterface.sol new file mode 100644 index 0000000..d8eab0a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/interfaces/WithdrawalInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.5.0; + +interface WithdrawalInterface { + /** + * @notice transfer LINK held by the contract belonging to msg.sender to + * another address + * @param recipient is the address to send the LINK to + * @param amount is the amount of LINK to send + */ + function withdraw(address recipient, uint256 amount) external; + + /** + * @notice query the available amount of LINK to withdraw by msg.sender + */ + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/BasicConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/BasicConsumer.sol new file mode 100644 index 0000000..bf1b763 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/BasicConsumer.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.5.0; + +import "./Consumer.sol"; + +contract BasicConsumer is Consumer { + + constructor(address _link, address _oracle, bytes32 _specId) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/ChainlinkTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/ChainlinkTestHelper.sol new file mode 100644 index 0000000..e760b83 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/ChainlinkTestHelper.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.5.0; + +import "../Chainlink.sol"; + +contract ChainlinkTestHelper { + using Chainlink for Chainlink.Request; + using CBOR_Chainlink for Buffer_Chainlink.buffer; + + Chainlink.Request private req; + + event RequestData(bytes payload); + + function closeEvent() public { + emit RequestData(req.buf.buf); + } + + function setBuffer(bytes memory data) public { + Chainlink.Request memory r2 = req; + r2.setBuffer(data); + req = r2; + } + + function add(string memory _key, string memory _value) public { + Chainlink.Request memory r2 = req; + r2.add(_key, _value); + req = r2; + } + + function addBytes(string memory _key, bytes memory _value) public { + Chainlink.Request memory r2 = req; + r2.addBytes(_key, _value); + req = r2; + } + + function addInt(string memory _key, int256 _value) public { + Chainlink.Request memory r2 = req; + r2.addInt(_key, _value); + req = r2; + } + + function addUint(string memory _key, uint256 _value) public { + Chainlink.Request memory r2 = req; + r2.addUint(_key, _value); + req = r2; + } + + // Temporarily have method receive bytes32[] memory until experimental + // string[] memory can be invoked from truffle tests. + function addStringArray(string memory _key, bytes32[] memory _values) public { + string[] memory strings = new string[](_values.length); + for (uint256 i = 0; i < _values.length; i++) { + strings[i] = bytes32ToString(_values[i]); + } + Chainlink.Request memory r2 = req; + r2.addStringArray(_key, strings); + req = r2; + } + + function bytes32ToString(bytes32 x) private pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint charCount = 0; + for (uint j = 0; j < 32; j++) { + byte char = byte(bytes32(uint(x) * 2 ** (8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/Consumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/Consumer.sol new file mode 100644 index 0000000..e937c17 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/Consumer.sol @@ -0,0 +1,55 @@ +pragma solidity ^0.5.0; + +import "../ChainlinkClient.sol"; + +contract Consumer is ChainlinkClient { + bytes32 internal specId; + bytes32 public currentPrice; + + event RequestFulfilled( + bytes32 indexed requestId, // User-defined ID + bytes32 indexed price + ); + + function requestEthereumPrice(string memory _currency, uint256 _payment) public { + requestEthereumPriceByCallback(_currency, _payment, address(this)); + } + + function requestEthereumPriceByCallback(string memory _currency, uint256 _payment, address _callback) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, _callback, this.fulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = _currency; + req.addStringArray("path", path); + sendChainlinkRequest(req, _payment); + } + + function cancelRequest( + address _oracle, + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(_oracle); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function withdrawLink() public { + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer"); + } + + function addExternalRequest(address _oracle, bytes32 _requestId) external { + addChainlinkExternalRequest(_oracle, _requestId); + } + + function fulfill(bytes32 _requestId, bytes32 _price) + public + recordChainlinkFulfillment(_requestId) + { + emit RequestFulfilled(_requestId, _price); + currentPrice = _price; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/EmptyAggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/EmptyAggregator.sol new file mode 100644 index 0000000..02331f3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/EmptyAggregator.sol @@ -0,0 +1,34 @@ +pragma solidity 0.5.0; + +import "../dev/CoordinatorInterface.sol"; + +/// Used to check the basic aggregator/coordinator interactions. It does nothing +/// but emit its messages as certain types of events. +contract EmptyAggregator { + + event InitiatedJob(bytes32 said); + + function initiateJob( + bytes32 _saId, bytes memory _serviceAgreementData) + public returns (bool success, bytes memory _) { + emit InitiatedJob(_saId); + success = true; + } + + event Fulfilled( + bytes32 requestId, + address oracle, + bool success, + bool complete, + bytes fulfillment); + + function fulfill(bytes32 _requestId, bytes32 _saId, address _oracle, + bytes32 _fulfillment) + public returns (bool success, bool complete, bytes memory response, + int256[] memory paymentAmounts) { + success = true; + complete = true; + response = abi.encode(_fulfillment); + emit Fulfilled(_requestId, _oracle, success, complete, response); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/GetterSetter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/GetterSetter.sol new file mode 100644 index 0000000..c765f5a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/GetterSetter.sol @@ -0,0 +1,45 @@ +pragma solidity 0.5.0; + +// GetterSetter is a contract to aid debugging and testing during development. +contract GetterSetter { + bytes32 public getBytes32; + uint256 public getUint256; + bytes32 public requestId; + bytes public getBytes; + + event SetBytes32(address indexed from, bytes32 indexed value); + event SetUint256(address indexed from, uint256 indexed value); + event SetBytes(address indexed from, bytes value); + + event Output(bytes32 b32, uint256 u256, bytes32 b322); + + function setBytes32(bytes32 _value) public { + getBytes32 = _value; + emit SetBytes32(msg.sender, _value); + } + + function requestedBytes32(bytes32 _requestId, bytes32 _value) public { + requestId = _requestId; + setBytes32(_value); + } + + function setBytes(bytes memory _value) public { + getBytes = _value; + emit SetBytes(msg.sender, _value); + } + + function requestedBytes(bytes32 _requestId, bytes memory _value) public { + requestId = _requestId; + setBytes(_value); + } + + function setUint256(uint256 _value) public { + getUint256 = _value; + emit SetUint256(msg.sender, _value); + } + + function requestedUint256(bytes32 _requestId, uint256 _value) public { + requestId = _requestId; + setUint256(_value); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousChainlink.sol new file mode 100644 index 0000000..b06d661 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousChainlink.sol @@ -0,0 +1,76 @@ +pragma solidity 0.5.0; + +import { CBOR as CBOR_Chainlink } from "../vendor/CBOR.sol"; +import { Buffer as Buffer_Chainlink } from "../vendor/Buffer.sol"; + +library MaliciousChainlink { + using CBOR_Chainlink for Buffer_Chainlink.buffer; + + struct Request { + bytes32 specId; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + Buffer_Chainlink.buffer buf; + } + + struct WithdrawRequest { + bytes32 specId; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + Buffer_Chainlink.buffer buf; + } + + function initializeWithdraw( + WithdrawRequest memory self, + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (MaliciousChainlink.WithdrawRequest memory) { + Buffer_Chainlink.init(self.buf, 128); + self.specId = _specId; + self.callbackAddress = _callbackAddress; + self.callbackFunctionId = _callbackFunction; + return self; + } + + function add(Request memory self, string memory _key, string memory _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeString(_value); + } + + function addBytes(Request memory self, string memory _key, bytes memory _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeBytes(_value); + } + + function addInt(Request memory self, string memory _key, int256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeInt(_value); + } + + function addUint(Request memory self, string memory _key, uint256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeUInt(_value); + } + + function addStringArray(Request memory self, string memory _key, string[] memory _values) + internal pure + { + self.buf.encodeString(_key); + self.buf.startArray(); + for (uint256 i = 0; i < _values.length; i++) { + self.buf.encodeString(_values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousChainlinkClient.sol new file mode 100644 index 0000000..ff5b070 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousChainlinkClient.sol @@ -0,0 +1,109 @@ +pragma solidity 0.5.0; + +import "./MaliciousChainlink.sol"; +import "../ChainlinkClient.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract MaliciousChainlinkClient is ChainlinkClient { + using MaliciousChainlink for MaliciousChainlink.Request; + using MaliciousChainlink for MaliciousChainlink.WithdrawRequest; + using Chainlink for Chainlink.Request; + using SafeMathChainlink for uint256; + + uint256 private maliciousRequests = 1; + mapping(bytes32 => address) private maliciousPendingRequests; + + function newWithdrawRequest( + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (MaliciousChainlink.WithdrawRequest memory) { + MaliciousChainlink.WithdrawRequest memory req; + return req.initializeWithdraw(_specId, _callbackAddress, _callbackFunction); + } + + function chainlinkTargetRequest(address _target, Chainlink.Request memory _req, uint256 _amount) + internal + returns(bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(_target, maliciousRequests)); + _req.nonce = maliciousRequests; + maliciousPendingRequests[requestId] = chainlinkOracleAddress(); + emit ChainlinkRequested(requestId); + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transferAndCall(chainlinkOracleAddress(), _amount, encodeTargetRequest(_req)), "Unable to transferAndCall to oracle"); + maliciousRequests += 1; + + return requestId; + } + + function chainlinkPriceRequest(Chainlink.Request memory _req, uint256 _amount) + internal + returns(bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, maliciousRequests)); + _req.nonce = maliciousRequests; + maliciousPendingRequests[requestId] = chainlinkOracleAddress(); + emit ChainlinkRequested(requestId); + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transferAndCall(chainlinkOracleAddress(), _amount, encodePriceRequest(_req)), "Unable to transferAndCall to oracle"); + maliciousRequests += 1; + + return requestId; + } + + function chainlinkWithdrawRequest(MaliciousChainlink.WithdrawRequest memory _req, uint256 _wei) + internal + returns(bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, maliciousRequests)); + _req.nonce = maliciousRequests; + maliciousPendingRequests[requestId] = chainlinkOracleAddress(); + emit ChainlinkRequested(requestId); + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transferAndCall(chainlinkOracleAddress(), _wei, encodeWithdrawRequest(_req)), "Unable to transferAndCall to oracle"); + maliciousRequests += 1; + return requestId; + } + + function encodeWithdrawRequest(MaliciousChainlink.WithdrawRequest memory _req) + internal pure returns (bytes memory) + { + return abi.encodeWithSelector( + bytes4(keccak256("withdraw(address,uint256)")), + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + _req.buf.buf); + } + + function encodeTargetRequest(Chainlink.Request memory _req) + internal pure returns (bytes memory) + { + return abi.encodeWithSelector( + bytes4(keccak256("oracleRequest(address,uint256,bytes32,address,bytes4,uint256,uint256,bytes)")), + 0, // overridden by onTokenTransfer + 0, // overridden by onTokenTransfer + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + 1, + _req.buf.buf); + } + + function encodePriceRequest(Chainlink.Request memory _req) + internal pure returns (bytes memory) + { + return abi.encodeWithSelector( + bytes4(keccak256("oracleRequest(address,uint256,bytes32,address,bytes4,uint256,uint256,bytes)")), + 0, // overridden by onTokenTransfer + 2000000000000000000, // overridden by onTokenTransfer + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + 1, + _req.buf.buf); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousConsumer.sol new file mode 100644 index 0000000..c14a4ac --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousConsumer.sol @@ -0,0 +1,57 @@ +pragma solidity 0.5.0; + +import "../ChainlinkClient.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract MaliciousConsumer is ChainlinkClient { + using SafeMathChainlink for uint256; + + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + uint256 private expiration; + + constructor(address _link, address _oracle) public payable { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + } + + function () external payable {} // solhint-disable-line no-empty-blocks + + function requestData(bytes32 _id, bytes memory _callbackFunc) public { + Chainlink.Request memory req = buildChainlinkRequest(_id, address(this), bytes4(keccak256(_callbackFunc))); + expiration = now.add(5 minutes); // solhint-disable-line not-rely-on-time + sendChainlinkRequest(req, ORACLE_PAYMENT); + } + + function assertFail(bytes32, bytes32) public pure { + assert(1 == 2); + } + + function cancelRequestOnFulfill(bytes32 _requestId, bytes32) public { + cancelChainlinkRequest( + _requestId, + ORACLE_PAYMENT, + this.cancelRequestOnFulfill.selector, + expiration); + } + + function remove() public { + selfdestruct(address(0)); + } + + function stealEthCall(bytes32 _requestId, bytes32) public recordChainlinkFulfillment(_requestId) { + (bool success,) = address(this).call.value(100)(""); // solhint-disable-line avoid-call-value + require(success, "Call failed"); + } + + function stealEthSend(bytes32 _requestId, bytes32) public recordChainlinkFulfillment(_requestId) { + // solhint-disable-next-line check-send-result + bool success = address(this).send(100); // solhint-disable-line multiple-sends + require(success, "Send failed"); + } + + function stealEthTransfer(bytes32 _requestId, bytes32) public recordChainlinkFulfillment(_requestId) { + address(this).transfer(100); + } + + function doesNothing(bytes32, bytes32) public pure {} // solhint-disable-line no-empty-blocks +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousRequester.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousRequester.sol new file mode 100644 index 0000000..53067c8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MaliciousRequester.sol @@ -0,0 +1,52 @@ +pragma solidity 0.5.0; + + +import "./MaliciousChainlinkClient.sol"; + + +contract MaliciousRequester is MaliciousChainlinkClient { + + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + uint256 private expiration; + + constructor(address _link, address _oracle) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + } + + function maliciousWithdraw() + public + { + MaliciousChainlink.WithdrawRequest memory req = newWithdrawRequest( + "specId", address(this), this.doesNothing.selector); + chainlinkWithdrawRequest(req, ORACLE_PAYMENT); + } + + function request(bytes32 _id, address _target, bytes memory _callbackFunc) public returns (bytes32 requestId) { + Chainlink.Request memory req = buildChainlinkRequest(_id, _target, bytes4(keccak256(_callbackFunc))); + expiration = now.add(5 minutes); // solhint-disable-line not-rely-on-time + requestId = sendChainlinkRequest(req, ORACLE_PAYMENT); + } + + function maliciousPrice(bytes32 _id) public returns (bytes32 requestId) { + Chainlink.Request memory req = buildChainlinkRequest(_id, address(this), this.doesNothing.selector); + requestId = chainlinkPriceRequest(req, ORACLE_PAYMENT); + } + + function maliciousTargetConsumer(address _target) public returns (bytes32 requestId) { + Chainlink.Request memory req = buildChainlinkRequest("specId", _target, bytes4(keccak256("fulfill(bytes32,bytes32)"))); + requestId = chainlinkTargetRequest(_target, req, ORACLE_PAYMENT); + } + + function maliciousRequestCancel(bytes32 _id, bytes memory _callbackFunc) public { + ChainlinkRequestInterface _oracle = ChainlinkRequestInterface(chainlinkOracleAddress()); + _oracle.cancelOracleRequest( + request(_id, address(this), _callbackFunc), + ORACLE_PAYMENT, + this.maliciousRequestCancel.selector, + expiration + ); + } + + function doesNothing(bytes32, bytes32) public pure {} // solhint-disable-line no-empty-blocks +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MeanAggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MeanAggregator.sol new file mode 100644 index 0000000..4c15858 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MeanAggregator.sol @@ -0,0 +1,75 @@ +pragma solidity 0.5.0; + +import "../dev/CoordinatorInterface.sol"; +import "../dev/ServiceAgreementDecoder.sol"; + +/// Computes the mean of the values the oracles pass it via fulfill method +contract MeanAggregator is ServiceAgreementDecoder { + + // Relies on Coordinator's authorization of the oracles (no need to track + // oracle authorization in this contract.) + + mapping(bytes32 /* service agreement ID */ => uint256) payment; + mapping(bytes32 /* service agreement ID */ => address[]) oracles; + mapping(bytes32 /* request ID */ => uint256) numberReported; + mapping(bytes32 /* request ID */ => mapping(address => uint256)) reportingOrder; + + // Current total for given request, divided by number of oracles reporting + mapping(bytes32 /* request ID */ => uint256) average; + // Remainder of total for given request from division by number of oracles. + mapping(bytes32 /* request ID */ => uint256) remainder; + + function initiateJob( + bytes32 _sAId, bytes memory _serviceAgreementData) + public returns (bool success, bytes memory message) { + ServiceAgreement memory serviceAgreement = decodeServiceAgreement(_serviceAgreementData); + + if (oracles[_sAId].length != 0) { + return (false, bytes("job already initiated")); + } + if (serviceAgreement.oracles.length == 0) { + return (false, bytes("must depend on at least one oracle")); + } + oracles[_sAId] = serviceAgreement.oracles; + payment[_sAId] = serviceAgreement.payment; + success = true; + } + + function fulfill(bytes32 _requestId, bytes32 _sAId, address _oracle, + bytes32 _value) public + returns (bool success, bool complete, bytes memory response, + int256[] memory paymentAmounts) { + if (reportingOrder[_requestId][_oracle] != 0 || + numberReported[_requestId] == oracles[_sAId].length) { + return (false, false, "oracle already reported", paymentAmounts); + } + uint256 oDividend = uint256(_value) / oracles[_sAId].length; + uint256 oRemainder = uint256(_value) % oracles[_sAId].length; + uint256 newRemainder = remainder[_requestId] + oRemainder; + uint256 newAverage = average[_requestId] + oDividend + (newRemainder / oracles[_sAId].length); + assert(newAverage >= average[_requestId]); // No overflow + average[_requestId] = newAverage; + remainder[_requestId] = newRemainder % oracles[_sAId].length; + numberReported[_requestId] += 1; + reportingOrder[_requestId][_oracle] = numberReported[_requestId]; + success = true; + complete = (numberReported[_requestId] == oracles[_sAId].length); + if (complete) { + response = abi.encode(average[_requestId]); + paymentAmounts = calculatePayments(_sAId, _requestId); + } + } + + function calculatePayments(bytes32 _sAId, bytes32 _requestId) private returns (int256[] memory paymentAmounts) { + paymentAmounts = new int256[](oracles[_sAId].length); + uint256 numOracles = oracles[_sAId].length; + uint256 totalPayment = payment[_sAId]; + for (uint256 oIdx = 0; oIdx < oracles[_sAId].length; oIdx++) { + // Linearly-decaying payout determined by each oracle's reportingIdx + uint256 reportingIdx = reportingOrder[_requestId][oracles[_sAId][oIdx]] - 1; + paymentAmounts[oIdx] = int256(2*(totalPayment/numOracles) - ( + (totalPayment * ((2*reportingIdx) + 1)) / (numOracles**2))); + delete reportingOrder[_requestId][oracles[_sAId][oIdx]]; + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MedianTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MedianTestHelper.sol new file mode 100644 index 0000000..0738731 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/MedianTestHelper.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.5.0; + +import "../Median.sol"; + +contract MedianTestHelper { + + function publicGet(int256[] memory _list) + public + pure + returns (int256) + { + return Median.calculate(_list); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/ServiceAgreementConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/ServiceAgreementConsumer.sol new file mode 100644 index 0000000..9be23e9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/tests/ServiceAgreementConsumer.sol @@ -0,0 +1,30 @@ +pragma solidity 0.5.0; + +import "../ChainlinkClient.sol"; + +contract ServiceAgreementConsumer is ChainlinkClient { + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + + bytes32 internal sAId; + bytes32 public currentPrice; + + constructor(address _link, address _coordinator, bytes32 _sAId) public { + setChainlinkToken(_link); + setChainlinkOracle(_coordinator); + sAId = _sAId; + } + + function requestEthereumPrice(string memory _currency) public { + Chainlink.Request memory req = buildChainlinkRequest(sAId, address(this), this.fulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + req.add("path", _currency); + sendChainlinkRequest(req, ORACLE_PAYMENT); + } + + function fulfill(bytes32 _requestId, bytes32 _price) + public + recordChainlinkFulfillment(_requestId) + { + currentPrice = _price; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/Buffer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/Buffer.sol new file mode 100644 index 0000000..ef74057 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/Buffer.sol @@ -0,0 +1,301 @@ +pragma solidity ^0.5.0; + +/** +* @dev A library for working with mutable byte buffers in Solidity. +* +* Byte buffers are mutable and expandable, and provide a variety of primitives +* for writing to them. At any time you can fetch a bytes object containing the +* current contents of the buffer. The bytes object should not be stored between +* operations, as it may change due to resizing of the buffer. +*/ +library Buffer { + /** + * @dev Represents a mutable buffer. Buffers have a current value (buf) and + * a capacity. The capacity may be longer than the current value, in + * which case it can be extended without the need to allocate more memory. + */ + struct buffer { + bytes buf; + uint capacity; + } + + /** + * @dev Initializes a buffer with an initial capacity. + * @param buf The buffer to initialize. + * @param capacity The number of bytes of space to allocate the buffer. + * @return The buffer, for chaining. + */ + function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + // Allocate space for the buffer data + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + mstore(0x40, add(32, add(ptr, capacity))) + } + return buf; + } + + /** + * @dev Initializes a new buffer from an existing bytes object. + * Changes to the buffer may mutate the original value. + * @param b The bytes object to initialize the buffer with. + * @return A new buffer. + */ + function fromBytes(bytes memory b) internal pure returns(buffer memory) { + buffer memory buf; + buf.buf = b; + buf.capacity = b.length; + return buf; + } + + function resize(buffer memory buf, uint capacity) private pure { + bytes memory oldbuf = buf.buf; + init(buf, capacity); + append(buf, oldbuf); + } + + function max(uint a, uint b) private pure returns(uint) { + if (a > b) { + return a; + } + return b; + } + + /** + * @dev Sets buffer length to 0. + * @param buf The buffer to truncate. + * @return The original buffer, for chaining.. + */ + function truncate(buffer memory buf) internal pure returns (buffer memory) { + assembly { + let bufptr := mload(buf) + mstore(bufptr, 0) + } + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) { + require(len <= data.length); + + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint dest; + uint src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(data, 32) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + + return buf; + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, len); + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, data.length); + } + + /** + * @dev Writes a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write the byte at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) { + if (off >= buf.capacity) { + resize(buf, buf.capacity * 2); + } + + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Address = buffer address + sizeof(buffer length) + off + let dest := add(add(bufptr, off), 32) + mstore8(dest, data) + // Update buffer length if we extended it + if eq(off, buflen) { + mstore(bufptr, add(buflen, 1)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) { + return writeUint8(buf, buf.buf.length, data); + } + + /** + * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (left-aligned). + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint mask = 256 ** len - 1; + // Right-align data + data = data >> (8 * (32 - len)); + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + sizeof(buffer length) + off + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) { + return write(buf, off, bytes32(data), 20); + } + + /** + * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chhaining. + */ + function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, bytes32(data), 20); + } + + /** + * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, 32); + } + + /** + * @dev Writes an integer to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (right-aligned). + * @return The original buffer, for chaining. + */ + function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint mask = 256 ** len - 1; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + off + sizeof(buffer length) + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer. + */ + function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { + return writeInt(buf, buf.buf.length, data, len); + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/CBOR.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/CBOR.sol new file mode 100644 index 0000000..9cce04a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/CBOR.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity >= 0.4.19 < 0.7.0; + +import { Buffer as BufferChainlink } from "./Buffer.sol"; + +library CBOR { + using BufferChainlink for BufferChainlink.buffer; + + uint8 private constant MAJOR_TYPE_INT = 0; + uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 private constant MAJOR_TYPE_BYTES = 2; + uint8 private constant MAJOR_TYPE_STRING = 3; + uint8 private constant MAJOR_TYPE_ARRAY = 4; + uint8 private constant MAJOR_TYPE_MAP = 5; + uint8 private constant MAJOR_TYPE_TAG = 6; + uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; + + uint8 private constant TAG_TYPE_BIGNUM = 2; + uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; + + function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure { + if(value <= 23) { + buf.appendUint8(uint8((major << 5) | value)); + } else if(value <= 0xFF) { + buf.appendUint8(uint8((major << 5) | 24)); + buf.appendInt(value, 1); + } else if(value <= 0xFFFF) { + buf.appendUint8(uint8((major << 5) | 25)); + buf.appendInt(value, 2); + } else if(value <= 0xFFFFFFFF) { + buf.appendUint8(uint8((major << 5) | 26)); + buf.appendInt(value, 4); + } else { + buf.appendUint8(uint8((major << 5) | 27)); + buf.appendInt(value, 8); + } + } + + function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure { + buf.appendUint8(uint8((major << 5) | 31)); + } + + function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure { + if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, value); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } + } + + function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure { + if(value < -0x10000000000000000) { + encodeSignedBigNum(buf, value); + } else if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, uint(value)); + } else if(value >= 0) { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(-1 - value)); + } + } + + function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); + buf.append(value); + } + + function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); + encodeBytes(buf, abi.encode(value)); + } + + function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); + encodeBytes(buf, abi.encode(uint(-1 - input))); + } + + function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); + buf.append(bytes(value)); + } + + function startArray(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); + } + + function startMap(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); + } + + function endSequence(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/ENSResolver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/ENSResolver.sol new file mode 100644 index 0000000..b80fd6d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/ENSResolver.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.5.0; + +contract ENSResolver { + function addr(bytes32 node) public view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/Ownable.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/Ownable.sol new file mode 100644 index 0000000..4d0929a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/Ownable.sol @@ -0,0 +1,65 @@ +pragma solidity ^0.5.0; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be aplied to your functions to restrict their use to + * the owner. + * + * This contract has been modified to remove the revokeOwnership function + */ +contract Ownable { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor () internal { + _owner = msg.sender; + emit OwnershipTransferred(address(0), _owner); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(isOwner(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Returns true if the caller is the current owner. + */ + function isOwner() public view returns (bool) { + return msg.sender == _owner; + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public onlyOwner { + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + */ + function _transferOwnership(address newOwner) internal { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + emit OwnershipTransferred(_owner, newOwner); + _owner = newOwner; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/SafeMathChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/SafeMathChainlink.sol new file mode 100644 index 0000000..da85c97 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/SafeMathChainlink.sol @@ -0,0 +1,107 @@ +pragma solidity ^0.5.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMathChainlink { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SafeMath: subtraction overflow"); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/SignedSafeMath.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/SignedSafeMath.sol new file mode 100644 index 0000000..68889f1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.5/vendor/SignedSafeMath.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.5.0; + +library SignedSafeMath { + + /** + * @dev Adds two int256s and makes sure the result doesn't overflow. Signed + * integers aren't supported by the SafeMath library, thus this method + * @param _a The first number to be added + * @param _a The second number to be added + */ + function add(int256 _a, int256 _b) + internal + pure + returns (int256) + { + // solium-disable-next-line zeppelin/no-arithmetic-operations + int256 c = _a + _b; + require((_b >= 0 && c >= _a) || (_b < 0 && c < _a), "SignedSafeMath: addition overflow"); + + return c; + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/AccessControlledAggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/AccessControlledAggregator.sol new file mode 100644 index 0000000..aa45c2c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/AccessControlledAggregator.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./FluxAggregator.sol"; +import "./SimpleReadAccessController.sol"; + +/** + * @title AccessControlled FluxAggregator contract + * @notice This contract requires addresses to be added to a controller + * in order to read the answers stored in the FluxAggregator contract + */ +contract AccessControlledAggregator is FluxAggregator, SimpleReadAccessController { + + /** + * @notice set up the aggregator with initial configuration + * @param _link The address of the LINK token + * @param _paymentAmount The amount paid of LINK paid to each oracle per submission, in wei (units of 10⁻¹⁸ LINK) + * @param _timeout is the number of seconds after the previous round that are + * allowed to lapse before allowing an oracle to skip an unfinished round + * @param _validator is an optional contract address for validating + * external validation of answers + * @param _minSubmissionValue is an immutable check for a lower bound of what + * submission values are accepted from an oracle + * @param _maxSubmissionValue is an immutable check for an upper bound of what + * submission values are accepted from an oracle + * @param _decimals represents the number of decimals to offset the answer by + * @param _description a short description of what is being reported + */ + constructor( + address _link, + uint128 _paymentAmount, + uint32 _timeout, + address _validator, + int256 _minSubmissionValue, + int256 _maxSubmissionValue, + uint8 _decimals, + string memory _description + ) public FluxAggregator( + _link, + _paymentAmount, + _timeout, + _validator, + _minSubmissionValue, + _maxSubmissionValue, + _decimals, + _description + ){} + + /** + * @notice get data about a round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * @param _roundId the round ID to retrieve the round data for + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. This is 0 + * if the round hasn't been started yet. + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. answeredInRound may be smaller than roundId when the round + * timed out. answerInRound is equal to roundId when the round didn't time out + * and was completed regularly. + * @dev overridden funcion to add the checkAccess() modifier + * @dev Note that for in-progress rounds (i.e. rounds that haven't yet + * received maxSubmissions) answer and updatedAt may change between queries. + */ + function getRoundData(uint80 _roundId) + public + view + override + checkAccess() + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return super.getRoundData(_roundId); + } + + /** + * @notice get data about the latest round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. Consumers are encouraged to + * use this more fully featured method over the "legacy" latestAnswer + * functions. Consumers are encouraged to check that they're receiving fresh + * data by inspecting the updatedAt and answeredInRound return values. + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. This is 0 + * if the round hasn't been started yet. + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. answeredInRound may be smaller than roundId when the round + * timed out. answerInRound is equal to roundId when the round didn't time out + * and was completed regularly. + * @dev overridden funcion to add the checkAccess() modifier + * @dev Note that for in-progress rounds (i.e. rounds that haven't yet + * received maxSubmissions) answer and updatedAt may change between queries. + */ + function latestRoundData() + public + view + override + checkAccess() + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return super.latestRoundData(); + } + + /** + * @notice get the most recently reported answer + * @dev overridden funcion to add the checkAccess() modifier + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestAnswer() + public + view + override + checkAccess() + returns (int256) + { + return super.latestAnswer(); + } + + /** + * @notice get the most recently reported round ID + * @dev overridden funcion to add the checkAccess() modifier + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestRound() + public + view + override + checkAccess() + returns (uint256) + { + return super.latestRound(); + } + + /** + * @notice get the most recent updated at timestamp + * @dev overridden funcion to add the checkAccess() modifier + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestTimestamp() + public + view + override + checkAccess() + returns (uint256) + { + return super.latestTimestamp(); + } + + /** + * @notice get past rounds answers + * @dev overridden funcion to add the checkAccess() modifier + * @param _roundId the round number to retrieve the answer for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getAnswer(uint256 _roundId) + public + view + override + checkAccess() + returns (int256) + { + return super.getAnswer(_roundId); + } + + /** + * @notice get timestamp when an answer was last updated + * @dev overridden funcion to add the checkAccess() modifier + * @param _roundId the round number to retrieve the updated timestamp for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getTimestamp(uint256 _roundId) + public + view + override + checkAccess() + returns (uint256) + { + return super.getTimestamp(_roundId); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/AggregatorFacade.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/AggregatorFacade.sol new file mode 100644 index 0000000..deae4cf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/AggregatorFacade.sol @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./interfaces/AggregatorV2V3Interface.sol"; + +/** + * @title A facade forAggregator versions to conform to the new v0.6 + * Aggregator V3 interface. + */ +contract AggregatorFacade is AggregatorV2V3Interface { + + AggregatorInterface public aggregator; + uint8 public override decimals; + string public override description; + + uint256 constant public override version = 2; + + // An error specific to the Aggregator V3 Interface, to prevent possible + // confusion around accidentally reading unset values as reported values. + string constant private V3_NO_DATA_ERROR = "No data present"; + + constructor( + address _aggregator, + uint8 _decimals, + string memory _description + ) public { + aggregator = AggregatorInterface(_aggregator); + decimals = _decimals; + description = _description; + } + + /** + * @notice get the latest completed round where the answer was updated + * @dev #[deprecated]. Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestRound() + external + view + virtual + override + returns (uint256) + { + return aggregator.latestRound(); + } + + /** + * @notice Reads the current answer from aggregator delegated to. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestAnswer() + external + view + virtual + override + returns (int256) + { + return aggregator.latestAnswer(); + } + + /** + * @notice Reads the last updated height from aggregator delegated to. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestTimestamp() + external + view + virtual + override + returns (uint256) + { + return aggregator.latestTimestamp(); + } + + /** + * @notice get data about the latest round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt value. + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is always equal to updatedAt because the underlying + * Aggregator contract does not expose this information. + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is always equal to roundId because the underlying + * Aggregator contract does not expose this information. + * @dev Note that for rounds that haven't yet received responses from all + * oracles, answer and updatedAt may change between queries. + */ + function latestRoundData() + external + view + virtual + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return _getRoundData(uint80(aggregator.latestRound())); + } + + /** + * @notice get past rounds answers + * @param _roundId the answer number to retrieve the answer for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getAnswer(uint256 _roundId) + external + view + virtual + override + returns (int256) + { + return aggregator.getAnswer(_roundId); + } + + /** + * @notice get block timestamp when an answer was last updated + * @param _roundId the answer number to retrieve the updated timestamp for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getTimestamp(uint256 _roundId) + external + view + virtual + override + returns (uint256) + { + return aggregator.getTimestamp(_roundId); + } + + /** + * @notice get data about a round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt value. + * @param _roundId the round ID to retrieve the round data for + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is always equal to updatedAt because the underlying + * Aggregator contract does not expose this information. + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is always equal to roundId because the underlying + * Aggregator contract does not expose this information. + * @dev Note that for rounds that haven't yet received responses from all + * oracles, answer and updatedAt may change between queries. + */ + function getRoundData(uint80 _roundId) + external + view + virtual + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return _getRoundData(_roundId); + } + + + /* + * Internal + */ + + function _getRoundData(uint80 _roundId) + internal + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + answer = aggregator.getAnswer(_roundId); + updatedAt = uint64(aggregator.getTimestamp(_roundId)); + + require(updatedAt > 0, V3_NO_DATA_ERROR); + + return (_roundId, answer, updatedAt, updatedAt, _roundId); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/AggregatorProxy.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/AggregatorProxy.sol new file mode 100644 index 0000000..73de220 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/AggregatorProxy.sol @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./Owned.sol"; +import "./interfaces/AggregatorV2V3Interface.sol"; + +/** + * @title A trusted proxy for updating where current answers are read from + * @notice This contract provides a consistent address for the + * CurrentAnwerInterface but delegates where it reads from to the owner, who is + * trusted to update it. + */ +contract AggregatorProxy is AggregatorV2V3Interface, Owned { + + struct Phase { + uint16 id; + AggregatorV2V3Interface aggregator; + } + Phase private currentPhase; + AggregatorV2V3Interface public proposedAggregator; + mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators; + + uint256 constant private PHASE_OFFSET = 64; + uint256 constant private PHASE_SIZE = 16; + uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1; + + constructor(address _aggregator) public Owned() { + setAggregator(_aggregator); + } + + /** + * @notice Reads the current answer from aggregator delegated to. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestAnswer() + public + view + virtual + override + returns (int256 answer) + { + return currentPhase.aggregator.latestAnswer(); + } + + /** + * @notice Reads the last updated height from aggregator delegated to. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestTimestamp() + public + view + virtual + override + returns (uint256 updatedAt) + { + return currentPhase.aggregator.latestTimestamp(); + } + + /** + * @notice get past rounds answers + * @param _roundId the answer number to retrieve the answer for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getAnswer(uint256 _roundId) + public + view + virtual + override + returns (int256 answer) + { + if (_roundId > MAX_ID) return 0; + + (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId); + AggregatorV2V3Interface aggregator = phaseAggregators[phaseId]; + if (address(aggregator) == address(0)) return 0; + + return aggregator.getAnswer(aggregatorRoundId); + } + + /** + * @notice get block timestamp when an answer was last updated + * @param _roundId the answer number to retrieve the updated timestamp for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getTimestamp(uint256 _roundId) + public + view + virtual + override + returns (uint256 updatedAt) + { + if (_roundId > MAX_ID) return 0; + + (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId); + AggregatorV2V3Interface aggregator = phaseAggregators[phaseId]; + if (address(aggregator) == address(0)) return 0; + + return aggregator.getTimestamp(aggregatorRoundId); + } + + /** + * @notice get the latest completed round where the answer was updated. This + * ID includes the proxy's phase, to make sure round IDs increase even when + * switching to a newly deployed aggregator. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestRound() + public + view + virtual + override + returns (uint256 roundId) + { + Phase memory phase = currentPhase; // cache storage reads + return addPhase(phase.id, uint64(phase.aggregator.latestRound())); + } + + /** + * @notice get data about a round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * Note that different underlying implementations of AggregatorV3Interface + * have slightly different semantics for some of the return values. Consumers + * should determine what implementations they expect to receive + * data from and validate that they can properly handle return data from all + * of them. + * @param _roundId the requested round ID as presented through the proxy, this + * is made up of the aggregator's round ID with the phase ID encoded in the + * two highest order bytes + * @return roundId is the round ID from the aggregator for which the data was + * retrieved combined with an phase to ensure that round IDs get larger as + * time moves forward. + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @dev Note that answer and updatedAt may change between queries. + */ + function getRoundData(uint80 _roundId) + public + view + virtual + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId); + + ( + roundId, + answer, + startedAt, + updatedAt, + answeredInRound + ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId); + + return addPhaseIds(roundId, answer, startedAt, updatedAt, answeredInRound, phaseId); + } + + /** + * @notice get data about the latest round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * Note that different underlying implementations of AggregatorV3Interface + * have slightly different semantics for some of the return values. Consumers + * should determine what implementations they expect to receive + * data from and validate that they can properly handle return data from all + * of them. + * @return roundId is the round ID from the aggregator for which the data was + * retrieved combined with an phase to ensure that round IDs get larger as + * time moves forward. + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @dev Note that answer and updatedAt may change between queries. + */ + function latestRoundData() + public + view + virtual + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + Phase memory current = currentPhase; // cache storage reads + + ( + roundId, + answer, + startedAt, + updatedAt, + answeredInRound + ) = current.aggregator.latestRoundData(); + + return addPhaseIds(roundId, answer, startedAt, updatedAt, answeredInRound, current.id); + } + + /** + * @notice Used if an aggregator contract has been proposed. + * @param _roundId the round ID to retrieve the round data for + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + */ + function proposedGetRoundData(uint80 _roundId) + public + view + virtual + hasProposal() + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return proposedAggregator.getRoundData(_roundId); + } + + /** + * @notice Used if an aggregator contract has been proposed. + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + */ + function proposedLatestRoundData() + public + view + virtual + hasProposal() + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return proposedAggregator.latestRoundData(); + } + + /** + * @notice returns the current phase's aggregator address. + */ + function aggregator() + external + view + returns (address) + { + return address(currentPhase.aggregator); + } + + /** + * @notice returns the current phase's ID. + */ + function phaseId() + external + view + returns (uint16) + { + return currentPhase.id; + } + + /** + * @notice represents the number of decimals the aggregator responses represent. + */ + function decimals() + external + view + override + returns (uint8) + { + return currentPhase.aggregator.decimals(); + } + + /** + * @notice the version number representing the type of aggregator the proxy + * points to. + */ + function version() + external + view + override + returns (uint256) + { + return currentPhase.aggregator.version(); + } + + /** + * @notice returns the description of the aggregator the proxy points to. + */ + function description() + external + view + override + returns (string memory) + { + return currentPhase.aggregator.description(); + } + + /** + * @notice Allows the owner to propose a new address for the aggregator + * @param _aggregator The new address for the aggregator contract + */ + function proposeAggregator(address _aggregator) + external + onlyOwner() + { + proposedAggregator = AggregatorV2V3Interface(_aggregator); + } + + /** + * @notice Allows the owner to confirm and change the address + * to the proposed aggregator + * @dev Reverts if the given address doesn't match what was previously + * proposed + * @param _aggregator The new address for the aggregator contract + */ + function confirmAggregator(address _aggregator) + external + onlyOwner() + { + require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator"); + delete proposedAggregator; + setAggregator(_aggregator); + } + + + /* + * Internal + */ + + function setAggregator(address _aggregator) + internal + { + uint16 id = currentPhase.id + 1; + currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator)); + phaseAggregators[id] = AggregatorV2V3Interface(_aggregator); + } + + function addPhase( + uint16 _phase, + uint64 _originalId + ) + internal + pure + returns (uint80) + { + return uint80(uint256(_phase) << PHASE_OFFSET | _originalId); + } + + function parseIds( + uint256 _roundId + ) + internal + pure + returns (uint16, uint64) + { + uint16 phaseId = uint16(_roundId >> PHASE_OFFSET); + uint64 aggregatorRoundId = uint64(_roundId); + + return (phaseId, aggregatorRoundId); + } + + function addPhaseIds( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound, + uint16 phaseId + ) + internal + pure + returns (uint80, int256, uint256, uint256, uint80) + { + return ( + addPhase(phaseId, uint64(roundId)), + answer, + startedAt, + updatedAt, + addPhase(phaseId, uint64(answeredInRound)) + ); + } + + /* + * Modifiers + */ + + modifier hasProposal() { + require(address(proposedAggregator) != address(0), "No proposed aggregator present"); + _; + } + +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/Chainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Chainlink.sol new file mode 100644 index 0000000..2665e67 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Chainlink.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import { CBORChainlink } from "./vendor/CBORChainlink.sol"; +import { BufferChainlink } from "./vendor/BufferChainlink.sol"; + +/** + * @title Library for common Chainlink functions + * @dev Uses imported CBOR library for encoding to buffer + */ +library Chainlink { + uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase + + using CBORChainlink for BufferChainlink.buffer; + + struct Request { + bytes32 id; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + BufferChainlink.buffer buf; + } + + /** + * @notice Initializes a Chainlink request + * @dev Sets the ID, callback address, and callback function signature on the request + * @param self The uninitialized request + * @param _id The Job Specification ID + * @param _callbackAddress The callback address + * @param _callbackFunction The callback function signature + * @return The initialized request + */ + function initialize( + Request memory self, + bytes32 _id, + address _callbackAddress, + bytes4 _callbackFunction + ) internal pure returns (Chainlink.Request memory) { + BufferChainlink.init(self.buf, defaultBufferSize); + self.id = _id; + self.callbackAddress = _callbackAddress; + self.callbackFunctionId = _callbackFunction; + return self; + } + + /** + * @notice Sets the data for the buffer without encoding CBOR on-chain + * @dev CBOR can be closed with curly-brackets {} or they can be left off + * @param self The initialized request + * @param _data The CBOR data + */ + function setBuffer(Request memory self, bytes memory _data) + internal pure + { + BufferChainlink.init(self.buf, _data.length); + BufferChainlink.append(self.buf, _data); + } + + /** + * @notice Adds a string value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The string value to add + */ + function add(Request memory self, string memory _key, string memory _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeString(_value); + } + + /** + * @notice Adds a bytes value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The bytes value to add + */ + function addBytes(Request memory self, string memory _key, bytes memory _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeBytes(_value); + } + + /** + * @notice Adds a int256 value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The int256 value to add + */ + function addInt(Request memory self, string memory _key, int256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeInt(_value); + } + + /** + * @notice Adds a uint256 value to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _value The uint256 value to add + */ + function addUint(Request memory self, string memory _key, uint256 _value) + internal pure + { + self.buf.encodeString(_key); + self.buf.encodeUInt(_value); + } + + /** + * @notice Adds an array of strings to the request with a given key name + * @param self The initialized request + * @param _key The name of the key + * @param _values The array of string values to add + */ + function addStringArray(Request memory self, string memory _key, string[] memory _values) + internal pure + { + self.buf.encodeString(_key); + self.buf.startArray(); + for (uint256 i = 0; i < _values.length; i++) { + self.buf.encodeString(_values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/ChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/ChainlinkClient.sol new file mode 100644 index 0000000..079e05e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/ChainlinkClient.sol @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./Chainlink.sol"; +import "./interfaces/ENSInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/PointerInterface.sol"; +import { ENSResolver as ENSResolver_Chainlink } from "./vendor/ENSResolver.sol"; + +/** + * @title The ChainlinkClient contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network + */ +contract ChainlinkClient { + using Chainlink for Chainlink.Request; + + uint256 constant internal LINK = 10**18; + uint256 constant private AMOUNT_OVERRIDE = 0; + address constant private SENDER_OVERRIDE = address(0); + uint256 constant private ARGS_VERSION = 1; + bytes32 constant private ENS_TOKEN_SUBNAME = keccak256("link"); + bytes32 constant private ENS_ORACLE_SUBNAME = keccak256("oracle"); + address constant private LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; + + ENSInterface private ens; + bytes32 private ensNode; + LinkTokenInterface private link; + ChainlinkRequestInterface private oracle; + uint256 private requestCount = 1; + mapping(bytes32 => address) private pendingRequests; + + event ChainlinkRequested(bytes32 indexed id); + event ChainlinkFulfilled(bytes32 indexed id); + event ChainlinkCancelled(bytes32 indexed id); + + /** + * @notice Creates a request that can hold additional parameters + * @param _specId The Job Specification ID that the request will be created for + * @param _callbackAddress The callback address that the response will be sent to + * @param _callbackFunctionSignature The callback function signature to use for the callback address + * @return A Chainlink Request struct in memory + */ + function buildChainlinkRequest( + bytes32 _specId, + address _callbackAddress, + bytes4 _callbackFunctionSignature + ) internal pure returns (Chainlink.Request memory) { + Chainlink.Request memory req; + return req.initialize(_specId, _callbackAddress, _callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `chainlinkRequestTo` with the stored oracle address + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequest(Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32) + { + return sendChainlinkRequestTo(address(oracle), _req, _payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param _oracle The address of the oracle for the request + * @param _req The initialized Chainlink Request + * @param _payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment) + internal + returns (bytes32 requestId) + { + requestId = keccak256(abi.encodePacked(this, requestCount)); + _req.nonce = requestCount; + pendingRequests[requestId] = _oracle; + emit ChainlinkRequested(requestId); + require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle"); + requestCount += 1; + + return requestId; + } + + /** + * @notice Allows a request to be cancelled if it has not been fulfilled + * @dev Requires keeping track of the expiration value emitted from the oracle contract. + * Deletes the request from the `pendingRequests` mapping. + * Emits ChainlinkCancelled event. + * @param _requestId The request ID + * @param _payment The amount of LINK sent for the request + * @param _callbackFunc The callback function specified for the request + * @param _expiration The time of the expiration for the request + */ + function cancelChainlinkRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunc, + uint256 _expiration + ) + internal + { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(pendingRequests[_requestId]); + delete pendingRequests[_requestId]; + emit ChainlinkCancelled(_requestId); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunc, _expiration); + } + + /** + * @notice Sets the stored oracle address + * @param _oracle The address of the oracle contract + */ + function setChainlinkOracle(address _oracle) internal { + oracle = ChainlinkRequestInterface(_oracle); + } + + /** + * @notice Sets the LINK token address + * @param _link The address of the LINK token contract + */ + function setChainlinkToken(address _link) internal { + link = LinkTokenInterface(_link); + } + + /** + * @notice Sets the Chainlink token address for the public + * network as given by the Pointer contract + */ + function setPublicChainlinkToken() internal { + setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkTokenAddress() + internal + view + returns (address) + { + return address(link); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function chainlinkOracleAddress() + internal + view + returns (address) + { + return address(oracle); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param _oracle The address of the oracle contract that will fulfill the request + * @param _requestId The request ID used for the response + */ + function addChainlinkExternalRequest(address _oracle, bytes32 _requestId) + internal + notPendingRequest(_requestId) + { + pendingRequests[_requestId] = _oracle; + } + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param _ens The address of the ENS contract + * @param _node The ENS node hash + */ + function useChainlinkWithENS(address _ens, bytes32 _node) + internal + { + ens = ENSInterface(_ens); + ensNode = _node; + bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ENS_TOKEN_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(linkSubnode)); + setChainlinkToken(resolver.addr(linkSubnode)); + updateChainlinkOracleWithENS(); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously + */ + function updateChainlinkOracleWithENS() + internal + { + bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(oracleSubnode)); + setChainlinkOracle(resolver.addr(oracleSubnode)); + } + + /** + * @notice Encodes the request to be sent to the oracle contract + * @dev The Chainlink node expects values to be in order for the request to be picked up. Order of types + * will be validated in the oracle contract. + * @param _req The initialized Chainlink Request + * @return The bytes payload for the `transferAndCall` method + */ + function encodeRequest(Chainlink.Request memory _req) + private + view + returns (bytes memory) + { + return abi.encodeWithSelector( + oracle.oracleRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + _req.id, + _req.callbackAddress, + _req.callbackFunctionId, + _req.nonce, + ARGS_VERSION, + _req.buf.buf); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param _requestId The request ID for fulfillment + */ + function validateChainlinkCallback(bytes32 _requestId) + internal + recordChainlinkFulfillment(_requestId) + // solhint-disable-next-line no-empty-blocks + {} + + /** + * @dev Reverts if the sender is not the oracle of the request. + * Emits ChainlinkFulfilled event. + * @param _requestId The request ID for fulfillment + */ + modifier recordChainlinkFulfillment(bytes32 _requestId) { + require(msg.sender == pendingRequests[_requestId], + "Source must be the oracle of the request"); + delete pendingRequests[_requestId]; + emit ChainlinkFulfilled(_requestId); + _; + } + + /** + * @dev Reverts if the request is already pending + * @param _requestId The request ID for fulfillment + */ + modifier notPendingRequest(bytes32 _requestId) { + require(pendingRequests[_requestId] == address(0), "Request is already pending"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/CheckedMath.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/CheckedMath.sol new file mode 100644 index 0000000..3f62279 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/CheckedMath.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/97894a140d2a698e5a0f913648a8f56d62277a70/contracts/math/SignedSafeMath.sol + +pragma solidity ^0.6.0; + +library CheckedMath { + + int256 constant internal INT256_MIN = -2**255; + + /** + * @dev Subtracts two signed integers, returns false 2nd param on overflow. + */ + function add( + int256 a, + int256 b + ) + internal + pure + returns (int256 result, bool ok) + { + int256 c = a + b; + if ((b >= 0 && c < a) || (b < 0 && c >= a)) return (0, false); + + return (c, true); + } + + /** + * @dev Subtracts two signed integers, returns false 2nd param on overflow. + */ + function sub( + int256 a, + int256 b + ) + internal + pure + returns (int256 result, bool ok) + { + int256 c = a - b; + if ((b < 0 && c <= a) || (b >= 0 && c > a)) return (0, false); + + return (c, true); + } + + + /** + * @dev Multiplies two signed integers, returns false 2nd param on overflow. + */ + function mul( + int256 a, + int256 b + ) + internal + pure + returns (int256 result, bool ok) + { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (0, true); + if (a == -1 && b == INT256_MIN) return (0, false); + + int256 c = a * b; + if (!(c / a == b)) return (0, false); + + return (c, true); + } + + /** + * @dev Divides two signed integers, returns false 2nd param on overflow. + */ + function div( + int256 a, + int256 b + ) + internal + pure + returns (int256 result, bool ok) + { + if (b == 0) return (0, false); + if (b == -1 && a == INT256_MIN) return (0, false); + + int256 c = a / b; + + return (c, true); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/Denominations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Denominations.sol new file mode 100644 index 0000000..339997d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Denominations.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +library Denominations { + address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 + address public constant USD = address(840); + address public constant GBP = address(826); + address public constant EUR = address(978); + address public constant JPY = address(392); + address public constant KRW = address(410); + address public constant CNY = address(156); + address public constant AUD = address(36); + address public constant CAD = address(124); + address public constant CHF = address(756); + address public constant ARS = address(32); + address public constant PHP = address(608); + address public constant NZD = address(554); + address public constant SGD = address(702); + address public constant NGN = address(566); + address public constant ZAR = address(710); + address public constant RUB = address(643); + address public constant INR = address(356); + address public constant BRL = address(986); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/DeviationFlaggingValidator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/DeviationFlaggingValidator.sol new file mode 100644 index 0000000..be2b2b1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/DeviationFlaggingValidator.sol @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./Owned.sol"; +import "./CheckedMath.sol"; +import "./interfaces/AggregatorValidatorInterface.sol"; +import "./interfaces/FlagsInterface.sol"; + +/** + * @title The Deviation Flagging Validator contract + * @notice Checks the current value against the previous value, and makes sure + * that it does not deviate outside of some relative range. If the deviation + * threshold is passed then the validator raises a flag on the designated + * flag contract. + */ +contract DeviationFlaggingValidator is Owned, AggregatorValidatorInterface { + using CheckedMath for int256; + + uint32 constant public THRESHOLD_MULTIPLIER = 100000; + + uint32 public flaggingThreshold; + FlagsInterface public flags; + + event FlaggingThresholdUpdated( + uint24 indexed previous, + uint24 indexed current + ); + event FlagsAddressUpdated( + address indexed previous, + address indexed current + ); + + int256 constant private INT256_MIN = -2**255; + + /** + * @notice sets up the validator with its threshold and flag address. + * @param _flags sets the address of the flags contract + * @param _flaggingThreshold sets the threshold that will trigger a flag to be + * raised. Setting the value of 100,000 is equivalent to tolerating a 100% + * change compared to the previous price. + */ + constructor( + address _flags, + uint24 _flaggingThreshold + ) + public + { + setFlagsAddress(_flags); + setFlaggingThreshold(_flaggingThreshold); + } + + /** + * @notice checks whether the parameters count as valid by comparing the + * difference change to the flagging threshold. + * @param _previousRoundId is ignored. + * @param _previousAnswer is used as the median of the difference with the + * current answer to determine if the deviation threshold has been exceeded. + * @param _roundId is ignored. + * @param _answer is the latest answer which is compared for a ratio of change + * to make sure it has not exceeded the flagging threshold. + */ + function validate( + uint256 _previousRoundId, + int256 _previousAnswer, + uint256 _roundId, + int256 _answer + ) + external + override + returns (bool) + { + if (!isValid(_previousRoundId, _previousAnswer, _roundId, _answer)) { + flags.raiseFlag(msg.sender); + return false; + } + + return true; + } + + /** + * @notice checks whether the parameters count as valid by comparing the + * difference change to the flagging threshold and raises a flag on the + * flagging contract if so. + * @param _previousAnswer is used as the median of the difference with the + * current answer to determine if the deviation threshold has been exceeded. + * @param _answer is the current answer which is compared for a ratio of + * change * to make sure it has not exceeded the flagging threshold. + */ + function isValid( + uint256 , + int256 _previousAnswer, + uint256 , + int256 _answer + ) + public + view + returns (bool) + { + if (_previousAnswer == 0) return true; + + (int256 change, bool changeOk) = _previousAnswer.sub(_answer); + (int256 ratioNumerator, bool numOk) = change.mul(THRESHOLD_MULTIPLIER); + (int256 ratio, bool ratioOk) = ratioNumerator.div(_previousAnswer); + (uint256 absRatio, bool absOk) = abs(ratio); + + return changeOk && numOk && ratioOk && absOk && absRatio <= flaggingThreshold; + } + + /** + * @notice updates the flagging threshold + * @param _flaggingThreshold sets the threshold that will trigger a flag to be + * raised. Setting the value of 100,000 is equivalent to tolerating a 100% + * change compared to the previous price. + */ + function setFlaggingThreshold(uint24 _flaggingThreshold) + public + onlyOwner() + { + uint24 previousFT = uint24(flaggingThreshold); + + if (previousFT != _flaggingThreshold) { + flaggingThreshold = _flaggingThreshold; + + emit FlaggingThresholdUpdated(previousFT, _flaggingThreshold); + } + } + + /** + * @notice updates the flagging contract address for raising flags + * @param _flags sets the address of the flags contract + */ + function setFlagsAddress(address _flags) + public + onlyOwner() + { + address previous = address(flags); + + if (previous != _flags) { + flags = FlagsInterface(_flags); + + emit FlagsAddressUpdated(previous, _flags); + } + } + + + // PRIVATE + + function abs( + int256 value + ) + private + pure + returns (uint256, bool) + { + if (value >= 0) return (uint256(value), true); + if (value == CheckedMath.INT256_MIN) return (0, false); + return (uint256(value * -1), true); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/EACAggregatorProxy.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/EACAggregatorProxy.sol new file mode 100644 index 0000000..f1c0e8b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/EACAggregatorProxy.sol @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./AggregatorProxy.sol"; +import "./interfaces/AccessControllerInterface.sol"; + +/** + * @title External Access Controlled Aggregator Proxy + * @notice A trusted proxy for updating where current answers are read from + * @notice This contract provides a consistent address for the + * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is + * trusted to update it. + * @notice Only access enabled addresses are allowed to access getters for + * aggregated answers and round information. + */ +contract EACAggregatorProxy is AggregatorProxy { + + AccessControllerInterface public accessController; + + constructor( + address _aggregator, + address _accessController + ) + public + AggregatorProxy(_aggregator) + { + setController(_accessController); + } + + /** + * @notice Allows the owner to update the accessController contract address. + * @param _accessController The new address for the accessController contract + */ + function setController(address _accessController) + public + onlyOwner() + { + accessController = AccessControllerInterface(_accessController); + } + + /** + * @notice Reads the current answer from aggregator delegated to. + * @dev overridden function to add the checkAccess() modifier + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestAnswer() + public + view + override + checkAccess() + returns (int256) + { + return super.latestAnswer(); + } + + /** + * @notice get the latest completed round where the answer was updated. This + * ID includes the proxy's phase, to make sure round IDs increase even when + * switching to a newly deployed aggregator. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestTimestamp() + public + view + override + checkAccess() + returns (uint256) + { + return super.latestTimestamp(); + } + + /** + * @notice get past rounds answers + * @param _roundId the answer number to retrieve the answer for + * @dev overridden function to add the checkAccess() modifier + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getAnswer(uint256 _roundId) + public + view + override + checkAccess() + returns (int256) + { + return super.getAnswer(_roundId); + } + + /** + * @notice get block timestamp when an answer was last updated + * @param _roundId the answer number to retrieve the updated timestamp for + * @dev overridden function to add the checkAccess() modifier + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getTimestamp(uint256 _roundId) + public + view + override + checkAccess() + returns (uint256) + { + return super.getTimestamp(_roundId); + } + + /** + * @notice get the latest completed round where the answer was updated + * @dev overridden function to add the checkAccess() modifier + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestRound() + public + view + override + checkAccess() + returns (uint256) + { + return super.latestRound(); + } + + /** + * @notice get data about a round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * Note that different underlying implementations of AggregatorV3Interface + * have slightly different semantics for some of the return values. Consumers + * should determine what implementations they expect to receive + * data from and validate that they can properly handle return data from all + * of them. + * @param _roundId the round ID to retrieve the round data for + * @return roundId is the round ID from the aggregator for which the data was + * retrieved combined with a phase to ensure that round IDs get larger as + * time moves forward. + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @dev Note that answer and updatedAt may change between queries. + */ + function getRoundData(uint80 _roundId) + public + view + checkAccess() + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return super.getRoundData(_roundId); + } + + /** + * @notice get data about the latest round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * Note that different underlying implementations of AggregatorV3Interface + * have slightly different semantics for some of the return values. Consumers + * should determine what implementations they expect to receive + * data from and validate that they can properly handle return data from all + * of them. + * @return roundId is the round ID from the aggregator for which the data was + * retrieved combined with a phase to ensure that round IDs get larger as + * time moves forward. + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @dev Note that answer and updatedAt may change between queries. + */ + function latestRoundData() + public + view + checkAccess() + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return super.latestRoundData(); + } + + /** + * @notice Used if an aggregator contract has been proposed. + * @param _roundId the round ID to retrieve the round data for + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + */ + function proposedGetRoundData(uint80 _roundId) + public + view + checkAccess() + hasProposal() + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return super.proposedGetRoundData(_roundId); + } + + /** + * @notice Used if an aggregator contract has been proposed. + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + */ + function proposedLatestRoundData() + public + view + checkAccess() + hasProposal() + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return super.proposedLatestRoundData(); + } + + /** + * @dev reverts if the caller does not have access by the accessController + * contract or is the contract itself. + */ + modifier checkAccess() { + AccessControllerInterface ac = accessController; + require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access"); + _; + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/Flags.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Flags.sol new file mode 100644 index 0000000..ce8c9d5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Flags.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + + +import "./SimpleReadAccessController.sol"; +import "./interfaces/AccessControllerInterface.sol"; +import "./interfaces/FlagsInterface.sol"; + + +/** + * @title The Flags contract + * @notice Allows flags to signal to any reader on the access control list. + * The owner can set flags, or designate other addresses to set flags. The + * owner must turn the flags off, other setters cannot. An expected pattern is + * to allow addresses to raise flags on themselves, so if you are subscribing to + * FlagOn events you should filter for addresses you care about. + */ +contract Flags is FlagsInterface, SimpleReadAccessController { + + AccessControllerInterface public raisingAccessController; + + mapping(address => bool) private flags; + + event FlagRaised( + address indexed subject + ); + event FlagLowered( + address indexed subject + ); + event RaisingAccessControllerUpdated( + address indexed previous, + address indexed current + ); + + /** + * @param racAddress address for the raising access controller. + */ + constructor( + address racAddress + ) + public + { + setRaisingAccessController(racAddress); + } + + /** + * @notice read the warning flag status of a contract address. + * @param subject The contract address being checked for a flag. + * @return A true value indicates that a flag was raised and a + * false value indicates that no flag was raised. + */ + function getFlag(address subject) + external + view + override + checkAccess() + returns (bool) + { + return flags[subject]; + } + + /** + * @notice read the warning flag status of a contract address. + * @param subjects An array of addresses being checked for a flag. + * @return An array of bools where a true value for any flag indicates that + * a flag was raised and a false value indicates that no flag was raised. + */ + function getFlags(address[] calldata subjects) + external + view + override + checkAccess() + returns (bool[] memory) + { + bool[] memory responses = new bool[](subjects.length); + for (uint256 i = 0; i < subjects.length; i++) { + responses[i] = flags[subjects[i]]; + } + return responses; + } + + /** + * @notice enable the warning flag for an address. + * Access is controlled by raisingAccessController, except for owner + * who always has access. + * @param subject The contract address whose flag is being raised + */ + function raiseFlag(address subject) + external + override + { + require(allowedToRaiseFlags(), "Not allowed to raise flags"); + + tryToRaiseFlag(subject); + } + + /** + * @notice enable the warning flags for multiple addresses. + * Access is controlled by raisingAccessController, except for owner + * who always has access. + * @param subjects List of the contract addresses whose flag is being raised + */ + function raiseFlags(address[] calldata subjects) + external + override + { + require(allowedToRaiseFlags(), "Not allowed to raise flags"); + + for (uint256 i = 0; i < subjects.length; i++) { + tryToRaiseFlag(subjects[i]); + } + } + + /** + * @notice allows owner to disable the warning flags for multiple addresses. + * @param subjects List of the contract addresses whose flag is being lowered + */ + function lowerFlags(address[] calldata subjects) + external + override + onlyOwner() + { + for (uint256 i = 0; i < subjects.length; i++) { + address subject = subjects[i]; + + if (flags[subject]) { + flags[subject] = false; + emit FlagLowered(subject); + } + } + } + + /** + * @notice allows owner to change the access controller for raising flags. + * @param racAddress new address for the raising access controller. + */ + function setRaisingAccessController( + address racAddress + ) + public + override + onlyOwner() + { + address previous = address(raisingAccessController); + + if (previous != racAddress) { + raisingAccessController = AccessControllerInterface(racAddress); + + emit RaisingAccessControllerUpdated(previous, racAddress); + } + } + + + // PRIVATE + + function allowedToRaiseFlags() + private + view + returns (bool) + { + return msg.sender == owner || + raisingAccessController.hasAccess(msg.sender, msg.data); + } + + function tryToRaiseFlag(address subject) + private + { + if (!flags[subject]) { + flags[subject] = true; + emit FlagRaised(subject); + } + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/FluxAggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/FluxAggregator.sol new file mode 100644 index 0000000..afe4731 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/FluxAggregator.sol @@ -0,0 +1,1053 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./Median.sol"; +import "./Owned.sol"; +import "./SafeMath128.sol"; +import "./SafeMath32.sol"; +import "./SafeMath64.sol"; +import "./interfaces/AggregatorV2V3Interface.sol"; +import "./interfaces/AggregatorValidatorInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./vendor/SafeMathChainlink.sol"; + +/** + * @title The Prepaid Aggregator contract + * @notice Handles aggregating data pushed in from off-chain, and unlocks + * payment for oracles as they report. Oracles' submissions are gathered in + * rounds, with each round aggregating the submissions for each oracle into a + * single answer. The latest aggregated answer is exposed as well as historical + * answers and their updated at timestamp. + */ +contract FluxAggregator is AggregatorV2V3Interface, Owned { + using SafeMathChainlink for uint256; + using SafeMath128 for uint128; + using SafeMath64 for uint64; + using SafeMath32 for uint32; + + struct Round { + int256 answer; + uint64 startedAt; + uint64 updatedAt; + uint32 answeredInRound; + } + + struct RoundDetails { + int256[] submissions; + uint32 maxSubmissions; + uint32 minSubmissions; + uint32 timeout; + uint128 paymentAmount; + } + + struct OracleStatus { + uint128 withdrawable; + uint32 startingRound; + uint32 endingRound; + uint32 lastReportedRound; + uint32 lastStartedRound; + int256 latestSubmission; + uint16 index; + address admin; + address pendingAdmin; + } + + struct Requester { + bool authorized; + uint32 delay; + uint32 lastStartedRound; + } + + struct Funds { + uint128 available; + uint128 allocated; + } + + LinkTokenInterface public linkToken; + AggregatorValidatorInterface public validator; + + // Round related params + uint128 public paymentAmount; + uint32 public maxSubmissionCount; + uint32 public minSubmissionCount; + uint32 public restartDelay; + uint32 public timeout; + uint8 public override decimals; + string public override description; + + int256 immutable public minSubmissionValue; + int256 immutable public maxSubmissionValue; + + uint256 constant public override version = 3; + + /** + * @notice To ensure owner isn't withdrawing required funds as oracles are + * submitting updates, we enforce that the contract maintains a minimum + * reserve of RESERVE_ROUNDS * oracleCount() LINK earmarked for payment to + * oracles. (Of course, this doesn't prevent the contract from running out of + * funds without the owner's intervention.) + */ + uint256 constant private RESERVE_ROUNDS = 2; + uint256 constant private MAX_ORACLE_COUNT = 77; + uint32 constant private ROUND_MAX = 2**32-1; + uint256 private constant VALIDATOR_GAS_LIMIT = 100000; + // An error specific to the Aggregator V3 Interface, to prevent possible + // confusion around accidentally reading unset values as reported values. + string constant private V3_NO_DATA_ERROR = "No data present"; + + uint32 private reportingRoundId; + uint32 internal latestRoundId; + mapping(address => OracleStatus) private oracles; + mapping(uint32 => Round) internal rounds; + mapping(uint32 => RoundDetails) internal details; + mapping(address => Requester) internal requesters; + address[] private oracleAddresses; + Funds private recordedFunds; + + event AvailableFundsUpdated( + uint256 indexed amount + ); + event RoundDetailsUpdated( + uint128 indexed paymentAmount, + uint32 indexed minSubmissionCount, + uint32 indexed maxSubmissionCount, + uint32 restartDelay, + uint32 timeout // measured in seconds + ); + event OraclePermissionsUpdated( + address indexed oracle, + bool indexed whitelisted + ); + event OracleAdminUpdated( + address indexed oracle, + address indexed newAdmin + ); + event OracleAdminUpdateRequested( + address indexed oracle, + address admin, + address newAdmin + ); + event SubmissionReceived( + int256 indexed submission, + uint32 indexed round, + address indexed oracle + ); + event RequesterPermissionsSet( + address indexed requester, + bool authorized, + uint32 delay + ); + event ValidatorUpdated( + address indexed previous, + address indexed current + ); + + /** + * @notice set up the aggregator with initial configuration + * @param _link The address of the LINK token + * @param _paymentAmount The amount paid of LINK paid to each oracle per submission, in wei (units of 10⁻¹⁸ LINK) + * @param _timeout is the number of seconds after the previous round that are + * allowed to lapse before allowing an oracle to skip an unfinished round + * @param _validator is an optional contract address for validating + * external validation of answers + * @param _minSubmissionValue is an immutable check for a lower bound of what + * submission values are accepted from an oracle + * @param _maxSubmissionValue is an immutable check for an upper bound of what + * submission values are accepted from an oracle + * @param _decimals represents the number of decimals to offset the answer by + * @param _description a short description of what is being reported + */ + constructor( + address _link, + uint128 _paymentAmount, + uint32 _timeout, + address _validator, + int256 _minSubmissionValue, + int256 _maxSubmissionValue, + uint8 _decimals, + string memory _description + ) public { + linkToken = LinkTokenInterface(_link); + updateFutureRounds(_paymentAmount, 0, 0, 0, _timeout); + setValidator(_validator); + minSubmissionValue = _minSubmissionValue; + maxSubmissionValue = _maxSubmissionValue; + decimals = _decimals; + description = _description; + rounds[0].updatedAt = uint64(block.timestamp.sub(uint256(_timeout))); + } + + /** + * @notice called by oracles when they have witnessed a need to update + * @param _roundId is the ID of the round this submission pertains to + * @param _submission is the updated data that the oracle is submitting + */ + function submit(uint256 _roundId, int256 _submission) + external + { + bytes memory error = validateOracleRound(msg.sender, uint32(_roundId)); + require(_submission >= minSubmissionValue, "value below minSubmissionValue"); + require(_submission <= maxSubmissionValue, "value above maxSubmissionValue"); + require(error.length == 0, string(error)); + + oracleInitializeNewRound(uint32(_roundId)); + recordSubmission(_submission, uint32(_roundId)); + (bool updated, int256 newAnswer) = updateRoundAnswer(uint32(_roundId)); + payOracle(uint32(_roundId)); + deleteRoundDetails(uint32(_roundId)); + if (updated) { + validateAnswer(uint32(_roundId), newAnswer); + } + } + + /** + * @notice called by the owner to remove and add new oracles as well as + * update the round related parameters that pertain to total oracle count + * @param _removed is the list of addresses for the new Oracles being removed + * @param _added is the list of addresses for the new Oracles being added + * @param _addedAdmins is the admin addresses for the new respective _added + * list. Only this address is allowed to access the respective oracle's funds + * @param _minSubmissions is the new minimum submission count for each round + * @param _maxSubmissions is the new maximum submission count for each round + * @param _restartDelay is the number of rounds an Oracle has to wait before + * they can initiate a round + */ + function changeOracles( + address[] calldata _removed, + address[] calldata _added, + address[] calldata _addedAdmins, + uint32 _minSubmissions, + uint32 _maxSubmissions, + uint32 _restartDelay + ) + external + onlyOwner() + { + for (uint256 i = 0; i < _removed.length; i++) { + removeOracle(_removed[i]); + } + + require(_added.length == _addedAdmins.length, "need same oracle and admin count"); + require(uint256(oracleCount()).add(_added.length) <= MAX_ORACLE_COUNT, "max oracles allowed"); + + for (uint256 i = 0; i < _added.length; i++) { + addOracle(_added[i], _addedAdmins[i]); + } + + updateFutureRounds(paymentAmount, _minSubmissions, _maxSubmissions, _restartDelay, timeout); + } + + /** + * @notice update the round and payment related parameters for subsequent + * rounds + * @param _paymentAmount is the payment amount for subsequent rounds + * @param _minSubmissions is the new minimum submission count for each round + * @param _maxSubmissions is the new maximum submission count for each round + * @param _restartDelay is the number of rounds an Oracle has to wait before + * they can initiate a round + */ + function updateFutureRounds( + uint128 _paymentAmount, + uint32 _minSubmissions, + uint32 _maxSubmissions, + uint32 _restartDelay, + uint32 _timeout + ) + public + onlyOwner() + { + uint32 oracleNum = oracleCount(); // Save on storage reads + require(_maxSubmissions >= _minSubmissions, "max must equal/exceed min"); + require(oracleNum >= _maxSubmissions, "max cannot exceed total"); + require(oracleNum == 0 || oracleNum > _restartDelay, "delay cannot exceed total"); + require(recordedFunds.available >= requiredReserve(_paymentAmount), "insufficient funds for payment"); + if (oracleCount() > 0) { + require(_minSubmissions > 0, "min must be greater than 0"); + } + + paymentAmount = _paymentAmount; + minSubmissionCount = _minSubmissions; + maxSubmissionCount = _maxSubmissions; + restartDelay = _restartDelay; + timeout = _timeout; + + emit RoundDetailsUpdated( + paymentAmount, + _minSubmissions, + _maxSubmissions, + _restartDelay, + _timeout + ); + } + + /** + * @notice the amount of payment yet to be withdrawn by oracles + */ + function allocatedFunds() + external + view + returns (uint128) + { + return recordedFunds.allocated; + } + + /** + * @notice the amount of future funding available to oracles + */ + function availableFunds() + external + view + returns (uint128) + { + return recordedFunds.available; + } + + /** + * @notice recalculate the amount of LINK available for payouts + */ + function updateAvailableFunds() + public + { + Funds memory funds = recordedFunds; + + uint256 nowAvailable = linkToken.balanceOf(address(this)).sub(funds.allocated); + + if (funds.available != nowAvailable) { + recordedFunds.available = uint128(nowAvailable); + emit AvailableFundsUpdated(nowAvailable); + } + } + + /** + * @notice returns the number of oracles + */ + function oracleCount() public view returns (uint8) { + return uint8(oracleAddresses.length); + } + + /** + * @notice returns an array of addresses containing the oracles on contract + */ + function getOracles() external view returns (address[] memory) { + return oracleAddresses; + } + + /** + * @notice get the most recently reported answer + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestAnswer() + public + view + virtual + override + returns (int256) + { + return rounds[latestRoundId].answer; + } + + /** + * @notice get the most recent updated at timestamp + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestTimestamp() + public + view + virtual + override + returns (uint256) + { + return rounds[latestRoundId].updatedAt; + } + + /** + * @notice get the ID of the last updated round + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestRound() + public + view + virtual + override + returns (uint256) + { + return latestRoundId; + } + + /** + * @notice get past rounds answers + * @param _roundId the round number to retrieve the answer for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getAnswer(uint256 _roundId) + public + view + virtual + override + returns (int256) + { + if (validRoundId(_roundId)) { + return rounds[uint32(_roundId)].answer; + } + return 0; + } + + /** + * @notice get timestamp when an answer was last updated + * @param _roundId the round number to retrieve the updated timestamp for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getTimestamp(uint256 _roundId) + public + view + virtual + override + returns (uint256) + { + if (validRoundId(_roundId)) { + return rounds[uint32(_roundId)].updatedAt; + } + return 0; + } + + /** + * @notice get data about a round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * @param _roundId the round ID to retrieve the round data for + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. This is 0 + * if the round hasn't been started yet. + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. answeredInRound may be smaller than roundId when the round + * timed out. answeredInRound is equal to roundId when the round didn't time out + * and was completed regularly. + * @dev Note that for in-progress rounds (i.e. rounds that haven't yet received + * maxSubmissions) answer and updatedAt may change between queries. + */ + function getRoundData(uint80 _roundId) + public + view + virtual + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + Round memory r = rounds[uint32(_roundId)]; + + require(r.answeredInRound > 0 && validRoundId(_roundId), V3_NO_DATA_ERROR); + + return ( + _roundId, + r.answer, + r.startedAt, + r.updatedAt, + r.answeredInRound + ); + } + + /** + * @notice get data about the latest round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. Consumers are encouraged to + * use this more fully featured method over the "legacy" latestRound/ + * latestAnswer/latestTimestamp functions. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * @return roundId is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. This is 0 + * if the round hasn't been started yet. + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. answeredInRound may be smaller than roundId when the round + * timed out. answeredInRound is equal to roundId when the round didn't time + * out and was completed regularly. + * @dev Note that for in-progress rounds (i.e. rounds that haven't yet + * received maxSubmissions) answer and updatedAt may change between queries. + */ + function latestRoundData() + public + view + virtual + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return getRoundData(latestRoundId); + } + + + /** + * @notice query the available amount of LINK for an oracle to withdraw + */ + function withdrawablePayment(address _oracle) + external + view + returns (uint256) + { + return oracles[_oracle].withdrawable; + } + + /** + * @notice transfers the oracle's LINK to another address. Can only be called + * by the oracle's admin. + * @param _oracle is the oracle whose LINK is transferred + * @param _recipient is the address to send the LINK to + * @param _amount is the amount of LINK to send + */ + function withdrawPayment(address _oracle, address _recipient, uint256 _amount) + external + { + require(oracles[_oracle].admin == msg.sender, "only callable by admin"); + + // Safe to downcast _amount because the total amount of LINK is less than 2^128. + uint128 amount = uint128(_amount); + uint128 available = oracles[_oracle].withdrawable; + require(available >= amount, "insufficient withdrawable funds"); + + oracles[_oracle].withdrawable = available.sub(amount); + recordedFunds.allocated = recordedFunds.allocated.sub(amount); + + assert(linkToken.transfer(_recipient, uint256(amount))); + } + + /** + * @notice transfers the owner's LINK to another address + * @param _recipient is the address to send the LINK to + * @param _amount is the amount of LINK to send + */ + function withdrawFunds(address _recipient, uint256 _amount) + external + onlyOwner() + { + uint256 available = uint256(recordedFunds.available); + require(available.sub(requiredReserve(paymentAmount)) >= _amount, "insufficient reserve funds"); + require(linkToken.transfer(_recipient, _amount), "token transfer failed"); + updateAvailableFunds(); + } + + /** + * @notice get the admin address of an oracle + * @param _oracle is the address of the oracle whose admin is being queried + */ + function getAdmin(address _oracle) + external + view + returns (address) + { + return oracles[_oracle].admin; + } + + /** + * @notice transfer the admin address for an oracle + * @param _oracle is the address of the oracle whose admin is being transferred + * @param _newAdmin is the new admin address + */ + function transferAdmin(address _oracle, address _newAdmin) + external + { + require(oracles[_oracle].admin == msg.sender, "only callable by admin"); + oracles[_oracle].pendingAdmin = _newAdmin; + + emit OracleAdminUpdateRequested(_oracle, msg.sender, _newAdmin); + } + + /** + * @notice accept the admin address transfer for an oracle + * @param _oracle is the address of the oracle whose admin is being transferred + */ + function acceptAdmin(address _oracle) + external + { + require(oracles[_oracle].pendingAdmin == msg.sender, "only callable by pending admin"); + oracles[_oracle].pendingAdmin = address(0); + oracles[_oracle].admin = msg.sender; + + emit OracleAdminUpdated(_oracle, msg.sender); + } + + /** + * @notice allows non-oracles to request a new round + */ + function requestNewRound() + external + returns (uint80) + { + require(requesters[msg.sender].authorized, "not authorized requester"); + + uint32 current = reportingRoundId; + require(rounds[current].updatedAt > 0 || timedOut(current), "prev round must be supersedable"); + + uint32 newRoundId = current.add(1); + requesterInitializeNewRound(newRoundId); + return newRoundId; + } + + /** + * @notice allows the owner to specify new non-oracles to start new rounds + * @param _requester is the address to set permissions for + * @param _authorized is a boolean specifying whether they can start new rounds or not + * @param _delay is the number of rounds the requester must wait before starting another round + */ + function setRequesterPermissions(address _requester, bool _authorized, uint32 _delay) + external + onlyOwner() + { + if (requesters[_requester].authorized == _authorized) return; + + if (_authorized) { + requesters[_requester].authorized = _authorized; + requesters[_requester].delay = _delay; + } else { + delete requesters[_requester]; + } + + emit RequesterPermissionsSet(_requester, _authorized, _delay); + } + + /** + * @notice called through LINK's transferAndCall to update available funds + * in the same transaction as the funds were transferred to the aggregator + * @param _data is mostly ignored. It is checked for length, to be sure + * nothing strange is passed in. + */ + function onTokenTransfer(address, uint256, bytes calldata _data) + external + { + require(_data.length == 0, "transfer doesn't accept calldata"); + updateAvailableFunds(); + } + + /** + * @notice a method to provide all current info oracles need. Intended only + * only to be callable by oracles. Not for use by contracts to read state. + * @param _oracle the address to look up information for. + */ + function oracleRoundState(address _oracle, uint32 _queriedRoundId) + external + view + returns ( + bool _eligibleToSubmit, + uint32 _roundId, + int256 _latestSubmission, + uint64 _startedAt, + uint64 _timeout, + uint128 _availableFunds, + uint8 _oracleCount, + uint128 _paymentAmount + ) + { + require(msg.sender == tx.origin, "off-chain reading only"); + + if (_queriedRoundId > 0) { + Round storage round = rounds[_queriedRoundId]; + RoundDetails storage details = details[_queriedRoundId]; + return ( + eligibleForSpecificRound(_oracle, _queriedRoundId), + _queriedRoundId, + oracles[_oracle].latestSubmission, + round.startedAt, + details.timeout, + recordedFunds.available, + oracleCount(), + (round.startedAt > 0 ? details.paymentAmount : paymentAmount) + ); + } else { + return oracleRoundStateSuggestRound(_oracle); + } + } + + /** + * @notice method to update the address which does external data validation. + * @param _newValidator designates the address of the new validation contract. + */ + function setValidator(address _newValidator) + public + onlyOwner() + { + address previous = address(validator); + + if (previous != _newValidator) { + validator = AggregatorValidatorInterface(_newValidator); + + emit ValidatorUpdated(previous, _newValidator); + } + } + + + /** + * Private + */ + + function initializeNewRound(uint32 _roundId) + private + { + updateTimedOutRoundInfo(_roundId.sub(1)); + + reportingRoundId = _roundId; + RoundDetails memory nextDetails = RoundDetails( + new int256[](0), + maxSubmissionCount, + minSubmissionCount, + timeout, + paymentAmount + ); + details[_roundId] = nextDetails; + rounds[_roundId].startedAt = uint64(block.timestamp); + + emit NewRound(_roundId, msg.sender, rounds[_roundId].startedAt); + } + + function oracleInitializeNewRound(uint32 _roundId) + private + { + if (!newRound(_roundId)) return; + uint256 lastStarted = oracles[msg.sender].lastStartedRound; // cache storage reads + if (_roundId <= lastStarted + restartDelay && lastStarted != 0) return; + + initializeNewRound(_roundId); + + oracles[msg.sender].lastStartedRound = _roundId; + } + + function requesterInitializeNewRound(uint32 _roundId) + private + { + if (!newRound(_roundId)) return; + uint256 lastStarted = requesters[msg.sender].lastStartedRound; // cache storage reads + require(_roundId > lastStarted + requesters[msg.sender].delay || lastStarted == 0, "must delay requests"); + + initializeNewRound(_roundId); + + requesters[msg.sender].lastStartedRound = _roundId; + } + + function updateTimedOutRoundInfo(uint32 _roundId) + private + { + if (!timedOut(_roundId)) return; + + uint32 prevId = _roundId.sub(1); + rounds[_roundId].answer = rounds[prevId].answer; + rounds[_roundId].answeredInRound = rounds[prevId].answeredInRound; + rounds[_roundId].updatedAt = uint64(block.timestamp); + + delete details[_roundId]; + } + + function eligibleForSpecificRound(address _oracle, uint32 _queriedRoundId) + private + view + returns (bool _eligible) + { + if (rounds[_queriedRoundId].startedAt > 0) { + return acceptingSubmissions(_queriedRoundId) && validateOracleRound(_oracle, _queriedRoundId).length == 0; + } else { + return delayed(_oracle, _queriedRoundId) && validateOracleRound(_oracle, _queriedRoundId).length == 0; + } + } + + function oracleRoundStateSuggestRound(address _oracle) + private + view + returns ( + bool _eligibleToSubmit, + uint32 _roundId, + int256 _latestSubmission, + uint64 _startedAt, + uint64 _timeout, + uint128 _availableFunds, + uint8 _oracleCount, + uint128 _paymentAmount + ) + { + Round storage round = rounds[0]; + OracleStatus storage oracle = oracles[_oracle]; + + bool shouldSupersede = oracle.lastReportedRound == reportingRoundId || !acceptingSubmissions(reportingRoundId); + // Instead of nudging oracles to submit to the next round, the inclusion of + // the shouldSupersede bool in the if condition pushes them towards + // submitting in a currently open round. + if (supersedable(reportingRoundId) && shouldSupersede) { + _roundId = reportingRoundId.add(1); + round = rounds[_roundId]; + + _paymentAmount = paymentAmount; + _eligibleToSubmit = delayed(_oracle, _roundId); + } else { + _roundId = reportingRoundId; + round = rounds[_roundId]; + + _paymentAmount = details[_roundId].paymentAmount; + _eligibleToSubmit = acceptingSubmissions(_roundId); + } + + if (validateOracleRound(_oracle, _roundId).length != 0) { + _eligibleToSubmit = false; + } + + return ( + _eligibleToSubmit, + _roundId, + oracle.latestSubmission, + round.startedAt, + details[_roundId].timeout, + recordedFunds.available, + oracleCount(), + _paymentAmount + ); + } + + function updateRoundAnswer(uint32 _roundId) + internal + returns (bool, int256) + { + if (details[_roundId].submissions.length < details[_roundId].minSubmissions) { + return (false, 0); + } + + int256 newAnswer = Median.calculateInplace(details[_roundId].submissions); + rounds[_roundId].answer = newAnswer; + rounds[_roundId].updatedAt = uint64(block.timestamp); + rounds[_roundId].answeredInRound = _roundId; + latestRoundId = _roundId; + + emit AnswerUpdated(newAnswer, _roundId, now); + + return (true, newAnswer); + } + + function validateAnswer( + uint32 _roundId, + int256 _newAnswer + ) + private + { + AggregatorValidatorInterface av = validator; // cache storage reads + if (address(av) == address(0)) return; + + uint32 prevRound = _roundId.sub(1); + uint32 prevAnswerRoundId = rounds[prevRound].answeredInRound; + int256 prevRoundAnswer = rounds[prevRound].answer; + // We do not want the validator to ever prevent reporting, so we limit its + // gas usage and catch any errors that may arise. + try av.validate{gas: VALIDATOR_GAS_LIMIT}( + prevAnswerRoundId, + prevRoundAnswer, + _roundId, + _newAnswer + ) {} catch {} + } + + function payOracle(uint32 _roundId) + private + { + uint128 payment = details[_roundId].paymentAmount; + Funds memory funds = recordedFunds; + funds.available = funds.available.sub(payment); + funds.allocated = funds.allocated.add(payment); + recordedFunds = funds; + oracles[msg.sender].withdrawable = oracles[msg.sender].withdrawable.add(payment); + + emit AvailableFundsUpdated(funds.available); + } + + function recordSubmission(int256 _submission, uint32 _roundId) + private + { + require(acceptingSubmissions(_roundId), "round not accepting submissions"); + + details[_roundId].submissions.push(_submission); + oracles[msg.sender].lastReportedRound = _roundId; + oracles[msg.sender].latestSubmission = _submission; + + emit SubmissionReceived(_submission, _roundId, msg.sender); + } + + function deleteRoundDetails(uint32 _roundId) + private + { + if (details[_roundId].submissions.length < details[_roundId].maxSubmissions) return; + + delete details[_roundId]; + } + + function timedOut(uint32 _roundId) + private + view + returns (bool) + { + uint64 startedAt = rounds[_roundId].startedAt; + uint32 roundTimeout = details[_roundId].timeout; + return startedAt > 0 && roundTimeout > 0 && startedAt.add(roundTimeout) < block.timestamp; + } + + function getStartingRound(address _oracle) + private + view + returns (uint32) + { + uint32 currentRound = reportingRoundId; + if (currentRound != 0 && currentRound == oracles[_oracle].endingRound) { + return currentRound; + } + return currentRound.add(1); + } + + function previousAndCurrentUnanswered(uint32 _roundId, uint32 _rrId) + private + view + returns (bool) + { + return _roundId.add(1) == _rrId && rounds[_rrId].updatedAt == 0; + } + + function requiredReserve(uint256 payment) + private + view + returns (uint256) + { + return payment.mul(oracleCount()).mul(RESERVE_ROUNDS); + } + + function addOracle( + address _oracle, + address _admin + ) + private + { + require(!oracleEnabled(_oracle), "oracle already enabled"); + + require(_admin != address(0), "cannot set admin to 0"); + require(oracles[_oracle].admin == address(0) || oracles[_oracle].admin == _admin, "owner cannot overwrite admin"); + + oracles[_oracle].startingRound = getStartingRound(_oracle); + oracles[_oracle].endingRound = ROUND_MAX; + oracles[_oracle].index = uint16(oracleAddresses.length); + oracleAddresses.push(_oracle); + oracles[_oracle].admin = _admin; + + emit OraclePermissionsUpdated(_oracle, true); + emit OracleAdminUpdated(_oracle, _admin); + } + + function removeOracle( + address _oracle + ) + private + { + require(oracleEnabled(_oracle), "oracle not enabled"); + + oracles[_oracle].endingRound = reportingRoundId.add(1); + address tail = oracleAddresses[uint256(oracleCount()).sub(1)]; + uint16 index = oracles[_oracle].index; + oracles[tail].index = index; + delete oracles[_oracle].index; + oracleAddresses[index] = tail; + oracleAddresses.pop(); + + emit OraclePermissionsUpdated(_oracle, false); + } + + function validateOracleRound(address _oracle, uint32 _roundId) + private + view + returns (bytes memory) + { + // cache storage reads + uint32 startingRound = oracles[_oracle].startingRound; + uint32 rrId = reportingRoundId; + + if (startingRound == 0) return "not enabled oracle"; + if (startingRound > _roundId) return "not yet enabled oracle"; + if (oracles[_oracle].endingRound < _roundId) return "no longer allowed oracle"; + if (oracles[_oracle].lastReportedRound >= _roundId) return "cannot report on previous rounds"; + if (_roundId != rrId && _roundId != rrId.add(1) && !previousAndCurrentUnanswered(_roundId, rrId)) return "invalid round to report"; + if (_roundId != 1 && !supersedable(_roundId.sub(1))) return "previous round not supersedable"; + } + + function supersedable(uint32 _roundId) + private + view + returns (bool) + { + return rounds[_roundId].updatedAt > 0 || timedOut(_roundId); + } + + function oracleEnabled(address _oracle) + private + view + returns (bool) + { + return oracles[_oracle].endingRound == ROUND_MAX; + } + + function acceptingSubmissions(uint32 _roundId) + private + view + returns (bool) + { + return details[_roundId].maxSubmissions != 0; + } + + function delayed(address _oracle, uint32 _roundId) + private + view + returns (bool) + { + uint256 lastStarted = oracles[_oracle].lastStartedRound; + return _roundId > lastStarted + restartDelay || lastStarted == 0; + } + + function newRound(uint32 _roundId) + private + view + returns (bool) + { + return _roundId == reportingRoundId.add(1); + } + + function validRoundId(uint256 _roundId) + private + pure + returns (bool) + { + return _roundId <= ROUND_MAX; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/KeeperBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/KeeperBase.sol new file mode 100644 index 0000000..ce9eccf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/KeeperBase.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +contract KeeperBase { + /** + * @notice method that allows it to be simulated via eth_call by checking that + * the sender is the zero address. + */ + function preventExecution() internal view { + require(tx.origin == address(0), "only for simulated backend"); + } + + /** + * @notice modifier that allows it to be simulated via eth_call by checking + * that the sender is the zero address. + */ + modifier cannotExecute() { + preventExecution(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/KeeperCompatible.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/KeeperCompatible.sol new file mode 100644 index 0000000..2a7ad60 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/KeeperCompatible.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./KeeperBase.sol"; +import "./interfaces/KeeperCompatibleInterface.sol"; + +abstract contract KeeperCompatible is KeeperBase, KeeperCompatibleInterface {} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/LinkTokenReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/LinkTokenReceiver.sol new file mode 100644 index 0000000..6009f6a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/LinkTokenReceiver.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +abstract contract LinkTokenReceiver { + + bytes4 constant private ORACLE_REQUEST_SELECTOR = 0x40429946; + uint256 constant private SELECTOR_LENGTH = 4; + uint256 constant private EXPECTED_REQUEST_WORDS = 2; + uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @dev The data payload's first 2 words will be overwritten by the `_sender` and `_amount` + * values to ensure correctness. Calls oracleRequest. + * @param _sender Address of the sender + * @param _amount Amount of LINK sent (specified in wei) + * @param _data Payload of the transaction + */ + function onTokenTransfer( + address _sender, + uint256 _amount, + bytes memory _data + ) + public + onlyLINK + validRequestLength(_data) + permittedFunctionsForLINK(_data) + { + assembly { + // solhint-disable-next-line avoid-low-level-calls + mstore(add(_data, 36), _sender) // ensure correct sender is passed + // solhint-disable-next-line avoid-low-level-calls + 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 + require(success, "Unable to create request"); + } + + function getChainlinkToken() public view virtual returns (address); + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + require(msg.sender == getChainlinkToken(), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `oracleRequest` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory _data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(_data, 32)) + } + require(funcSelector == ORACLE_REQUEST_SELECTOR, "Must use whitelisted functions"); + _; + } + + /** + * @dev Reverts if the given payload is less than needed to create a request + * @param _data The request payload + */ + modifier validRequestLength(bytes memory _data) { + require(_data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/Median.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Median.sol new file mode 100644 index 0000000..d75b5b6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Median.sol @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./vendor/SafeMathChainlink.sol"; +import "./SignedSafeMath.sol"; + +library Median { + using SignedSafeMath for int256; + + int256 constant INT_MAX = 2**255-1; + + /** + * @notice Returns the sorted middle, or the average of the two middle indexed items if the + * array has an even number of elements. + * @dev The list passed as an argument isn't modified. + * @dev This algorithm has expected runtime O(n), but for adversarially chosen inputs + * the runtime is O(n^2). + * @param list The list of elements to compare + */ + function calculate(int256[] memory list) + internal + pure + returns (int256) + { + return calculateInplace(copy(list)); + } + + /** + * @notice See documentation for function calculate. + * @dev The list passed as an argument may be permuted. + */ + function calculateInplace(int256[] memory list) + internal + pure + returns (int256) + { + require(0 < list.length, "list must not be empty"); + uint256 len = list.length; + uint256 middleIndex = len / 2; + if (len % 2 == 0) { + int256 median1; + int256 median2; + (median1, median2) = quickselectTwo(list, 0, len - 1, middleIndex - 1, middleIndex); + return SignedSafeMath.avg(median1, median2); + } else { + return quickselect(list, 0, len - 1, middleIndex); + } + } + + /** + * @notice Maximum length of list that shortSelectTwo can handle + */ + uint256 constant SHORTSELECTTWO_MAX_LENGTH = 7; + + /** + * @notice Select the k1-th and k2-th element from list of length at most 7 + * @dev Uses an optimal sorting network + */ + function shortSelectTwo( + int256[] memory list, + uint256 lo, + uint256 hi, + uint256 k1, + uint256 k2 + ) + private + pure + returns (int256 k1th, int256 k2th) + { + // Uses an optimal sorting network (https://en.wikipedia.org/wiki/Sorting_network) + // for lists of length 7. Network layout is taken from + // http://jgamble.ripco.net/cgi-bin/nw.cgi?inputs=7&algorithm=hibbard&output=svg + + uint256 len = hi + 1 - lo; + int256 x0 = list[lo + 0]; + int256 x1 = 1 < len ? list[lo + 1] : INT_MAX; + int256 x2 = 2 < len ? list[lo + 2] : INT_MAX; + int256 x3 = 3 < len ? list[lo + 3] : INT_MAX; + int256 x4 = 4 < len ? list[lo + 4] : INT_MAX; + int256 x5 = 5 < len ? list[lo + 5] : INT_MAX; + int256 x6 = 6 < len ? list[lo + 6] : INT_MAX; + + if (x0 > x1) {(x0, x1) = (x1, x0);} + if (x2 > x3) {(x2, x3) = (x3, x2);} + if (x4 > x5) {(x4, x5) = (x5, x4);} + if (x0 > x2) {(x0, x2) = (x2, x0);} + if (x1 > x3) {(x1, x3) = (x3, x1);} + if (x4 > x6) {(x4, x6) = (x6, x4);} + if (x1 > x2) {(x1, x2) = (x2, x1);} + if (x5 > x6) {(x5, x6) = (x6, x5);} + if (x0 > x4) {(x0, x4) = (x4, x0);} + if (x1 > x5) {(x1, x5) = (x5, x1);} + if (x2 > x6) {(x2, x6) = (x6, x2);} + if (x1 > x4) {(x1, x4) = (x4, x1);} + if (x3 > x6) {(x3, x6) = (x6, x3);} + if (x2 > x4) {(x2, x4) = (x4, x2);} + if (x3 > x5) {(x3, x5) = (x5, x3);} + if (x3 > x4) {(x3, x4) = (x4, x3);} + + uint256 index1 = k1 - lo; + if (index1 == 0) {k1th = x0;} + else if (index1 == 1) {k1th = x1;} + else if (index1 == 2) {k1th = x2;} + else if (index1 == 3) {k1th = x3;} + else if (index1 == 4) {k1th = x4;} + else if (index1 == 5) {k1th = x5;} + else if (index1 == 6) {k1th = x6;} + else {revert("k1 out of bounds");} + + uint256 index2 = k2 - lo; + if (k1 == k2) {return (k1th, k1th);} + else if (index2 == 0) {return (k1th, x0);} + else if (index2 == 1) {return (k1th, x1);} + else if (index2 == 2) {return (k1th, x2);} + else if (index2 == 3) {return (k1th, x3);} + else if (index2 == 4) {return (k1th, x4);} + else if (index2 == 5) {return (k1th, x5);} + else if (index2 == 6) {return (k1th, x6);} + else {revert("k2 out of bounds");} + } + + /** + * @notice Selects the k-th ranked element from list, looking only at indices between lo and hi + * (inclusive). Modifies list in-place. + */ + function quickselect(int256[] memory list, uint256 lo, uint256 hi, uint256 k) + private + pure + returns (int256 kth) + { + require(lo <= k); + require(k <= hi); + while (lo < hi) { + if (hi - lo < SHORTSELECTTWO_MAX_LENGTH) { + int256 ignore; + (kth, ignore) = shortSelectTwo(list, lo, hi, k, k); + return kth; + } + uint256 pivotIndex = partition(list, lo, hi); + if (k <= pivotIndex) { + // since pivotIndex < (original hi passed to partition), + // termination is guaranteed in this case + hi = pivotIndex; + } else { + // since (original lo passed to partition) <= pivotIndex, + // termination is guaranteed in this case + lo = pivotIndex + 1; + } + } + return list[lo]; + } + + /** + * @notice Selects the k1-th and k2-th ranked elements from list, looking only at indices between + * lo and hi (inclusive). Modifies list in-place. + */ + function quickselectTwo( + int256[] memory list, + uint256 lo, + uint256 hi, + uint256 k1, + uint256 k2 + ) + internal // for testing + pure + returns (int256 k1th, int256 k2th) + { + require(k1 < k2); + require(lo <= k1 && k1 <= hi); + require(lo <= k2 && k2 <= hi); + + while (true) { + if (hi - lo < SHORTSELECTTWO_MAX_LENGTH) { + return shortSelectTwo(list, lo, hi, k1, k2); + } + uint256 pivotIdx = partition(list, lo, hi); + if (k2 <= pivotIdx) { + hi = pivotIdx; + } else if (pivotIdx < k1) { + lo = pivotIdx + 1; + } else { + assert(k1 <= pivotIdx && pivotIdx < k2); + k1th = quickselect(list, lo, pivotIdx, k1); + k2th = quickselect(list, pivotIdx + 1, hi, k2); + return (k1th, k2th); + } + } + } + + /** + * @notice Partitions list in-place using Hoare's partitioning scheme. + * Only elements of list between indices lo and hi (inclusive) will be modified. + * Returns an index i, such that: + * - lo <= i < hi + * - forall j in [lo, i]. list[j] <= list[i] + * - forall j in [i, hi]. list[i] <= list[j] + */ + function partition(int256[] memory list, uint256 lo, uint256 hi) + private + pure + returns (uint256) + { + // We don't care about overflow of the addition, because it would require a list + // larger than any feasible computer's memory. + int256 pivot = list[(lo + hi) / 2]; + lo -= 1; // this can underflow. that's intentional. + hi += 1; + while (true) { + do { + lo += 1; + } while (list[lo] < pivot); + do { + hi -= 1; + } while (list[hi] > pivot); + if (lo < hi) { + (list[lo], list[hi]) = (list[hi], list[lo]); + } else { + // Let orig_lo and orig_hi be the original values of lo and hi passed to partition. + // Then, hi < orig_hi, because hi decreases *strictly* monotonically + // in each loop iteration and + // - either list[orig_hi] > pivot, in which case the first loop iteration + // will achieve hi < orig_hi; + // - or list[orig_hi] <= pivot, in which case at least two loop iterations are + // needed: + // - lo will have to stop at least once in the interval + // [orig_lo, (orig_lo + orig_hi)/2] + // - (orig_lo + orig_hi)/2 < orig_hi + return hi; + } + } + } + + /** + * @notice Makes an in-memory copy of the array passed in + * @param list Reference to the array to be copied + */ + function copy(int256[] memory list) + private + pure + returns(int256[] memory) + { + int256[] memory list2 = new int256[](list.length); + for (uint256 i = 0; i < list.length; i++) { + list2[i] = list[i]; + } + return list2; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/Oracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Oracle.sol new file mode 100644 index 0000000..967114f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Oracle.sol @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./LinkTokenReceiver.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/OracleInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/WithdrawalInterface.sol"; +import "./vendor/Ownable.sol"; +import "./vendor/SafeMathChainlink.sol"; + +/** + * @title The Chainlink Oracle contract + * @notice Node operators can deploy this contract to fulfill requests sent to them + */ +contract Oracle is ChainlinkRequestInterface, OracleInterface, Ownable, LinkTokenReceiver, WithdrawalInterface { + using SafeMathChainlink for uint256; + + uint256 constant public EXPIRY_TIME = 5 minutes; + uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; + // We initialize fields to 1 instead of 0 so that the first invocation + // does not cost more gas. + uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; + + LinkTokenInterface internal LinkToken; + mapping(bytes32 => bytes32) private commitments; + mapping(address => bool) private authorizedNodes; + uint256 private withdrawableTokens = ONE_FOR_CONSISTENT_GAS_COST; + + 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 + Ownable() + { + 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] == 0, "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + uint256 expiration = now.add(EXPIRY_TIME); + + commitments[requestId] = keccak256( + abi.encodePacked( + _payment, + _callbackAddress, + _callbackFunctionId, + expiration + ) + ); + + 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 _payment The payment amount that will be released for the oracle (specified in wei) + * @param _callbackAddress The callback address to call for fulfillment + * @param _callbackFunctionId The callback function ID to use for fulfillment + * @param _expiration The expiration that the node should respond by before the requester can cancel + * @param _data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest( + bytes32 _requestId, + uint256 _payment, + address _callbackAddress, + bytes4 _callbackFunctionId, + uint256 _expiration, + bytes32 _data + ) + external + onlyAuthorizedNode + override + isValidRequest(_requestId) + returns (bool) + { + bytes32 paramsHash = keccak256( + abi.encodePacked( + _payment, + _callbackAddress, + _callbackFunctionId, + _expiration + ) + ); + require(commitments[_requestId] == paramsHash, "Params do not match request ID"); + withdrawableTokens = withdrawableTokens.add(_payment); + 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, ) = _callbackAddress.call(abi.encodeWithSelector(_callbackFunctionId, _requestId, _data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Use this to check if a node is authorized for fulfilling requests + * @param _node The address of the Chainlink node + * @return The authorization status of the node + */ + function getAuthorizationStatus(address _node) + external + view + override + returns (bool) + { + return authorizedNodes[_node]; + } + + /** + * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. + * @param _node The address of the Chainlink node + * @param _allowed Bool value to determine if the node can fulfill requests + */ + function setFulfillmentPermission(address _node, bool _allowed) + external + override + onlyOwner() + { + authorizedNodes[_node] = _allowed; + } + + /** + * @notice Allows the node operator to withdraw earned LINK to a given address + * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node + * @param _recipient The address to send the LINK token to + * @param _amount The amount to send (specified in wei) + */ + function withdraw(address _recipient, uint256 _amount) + external + override(OracleInterface, WithdrawalInterface) + onlyOwner + hasAvailableFunds(_amount) + { + withdrawableTokens = withdrawableTokens.sub(_amount); + assert(LinkToken.transfer(_recipient, _amount)); + } + + /** + * @notice Displays the amount of LINK that is available for the node operator to withdraw + * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage + * @return The amount of withdrawable LINK on the contract + */ + function withdrawable() + external + view + override(OracleInterface, WithdrawalInterface) + onlyOwner() + returns (uint256) + { + return withdrawableTokens.sub(ONE_FOR_CONSISTENT_GAS_COST); + } + + /** + * @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 _callbackFunc The requester's specified callback address + * @param _expiration The time of the expiration for the request + */ + function cancelOracleRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunc, + uint256 _expiration + ) + external + override + { + bytes32 paramsHash = keccak256( + abi.encodePacked( + _payment, + msg.sender, + _callbackFunc, + _expiration) + ); + require(paramsHash == commitments[_requestId], "Params do not match request 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 amount requested is greater than withdrawable balance + * @param _amount The given amount to compare to `withdrawableTokens` + */ + modifier hasAvailableFunds(uint256 _amount) { + require(withdrawableTokens >= _amount.add(ONE_FOR_CONSISTENT_GAS_COST), "Amount requested is greater than withdrawable balance"); + _; + } + + /** + * @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] != 0, "Must have a valid requestId"); + _; + } + + /** + * @dev Reverts if `msg.sender` is not authorized to fulfill requests + */ + modifier onlyAuthorizedNode() { + require(authorizedNodes[msg.sender] || msg.sender == owner(), "Not an authorized node to fulfill requests"); + _; + } + + /** + * @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/lib/chainlink-brownie-contracts/contracts/src/v0.6/Owned.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Owned.sol new file mode 100644 index 0000000..0dc7c46 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/Owned.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.6.0 <0.8.0; + +/** + * @title The Owned contract + * @notice A contract with helpers for basic contract ownership. + */ +contract Owned { + + address public owner; + address private pendingOwner; + + event OwnershipTransferRequested( + address indexed from, + address indexed to + ); + event OwnershipTransferred( + address indexed from, + address indexed to + ); + + constructor() public { + owner = msg.sender; + } + + /** + * @dev Allows an owner to begin transferring ownership to a new address, + * pending. + */ + function transferOwnership(address _to) + external + onlyOwner() + { + pendingOwner = _to; + + emit OwnershipTransferRequested(owner, _to); + } + + /** + * @dev Allows an ownership transfer to be completed by the recipient. + */ + function acceptOwnership() + external + { + require(msg.sender == pendingOwner, "Must be proposed owner"); + + address oldOwner = owner; + owner = msg.sender; + pendingOwner = address(0); + + emit OwnershipTransferred(oldOwner, msg.sender); + } + + /** + * @dev Reverts if called by anyone other than the contract owner. + */ + modifier onlyOwner() { + require(msg.sender == owner, "Only callable by owner"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/PreCoordinator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/PreCoordinator.sol new file mode 100644 index 0000000..ee13c72 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/PreCoordinator.sol @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./ChainlinkClient.sol"; +import "./LinkTokenReceiver.sol"; +import "./Median.sol"; +import "./vendor/Ownable.sol"; +import "./vendor/SafeMathChainlink.sol"; + +/** + * @title PreCoordinator is a contract that builds on-chain service agreements + * using the current architecture of 1 request to 1 oracle contract. + * @dev This contract accepts requests as service agreement IDs and loops over + * the corresponding list of oracles to create distinct requests to each one. + */ +contract PreCoordinator is ChainlinkClient, Ownable, ChainlinkRequestInterface, LinkTokenReceiver { + using SafeMathChainlink for uint256; + + uint256 constant private MAX_ORACLE_COUNT = 45; + + uint256 private globalNonce; + + struct ServiceAgreement { + uint256 totalPayment; + uint256 minResponses; + address[] oracles; + bytes32[] jobIds; + uint256[] payments; + } + + struct Requester { + bytes4 callbackFunctionId; + address sender; + address callbackAddress; + int256[] responses; + } + + // Service Agreement ID => ServiceAgreement + mapping(bytes32 => ServiceAgreement) internal serviceAgreements; + // Local Request ID => Service Agreement ID + mapping(bytes32 => bytes32) internal serviceAgreementRequests; + // Requester's Request ID => Requester + mapping(bytes32 => Requester) internal requesters; + // Local Request ID => Requester's Request ID + mapping(bytes32 => bytes32) internal requests; + + event NewServiceAgreement(bytes32 indexed saId, uint256 payment, uint256 minresponses); + event ServiceAgreementRequested(bytes32 indexed saId, bytes32 indexed requestId, uint256 payment); + event ServiceAgreementResponseReceived(bytes32 indexed saId, bytes32 indexed requestId, address indexed oracle, int256 answer); + event ServiceAgreementAnswerUpdated(bytes32 indexed saId, bytes32 indexed requestId, int256 answer); + event ServiceAgreementDeleted(bytes32 indexed saId); + + /** + * @notice Deploy the contract with a specified address for the LINK + * and Oracle contract addresses + * @dev Sets the storage for the specified addresses + * @param _link The address of the LINK token contract + */ + constructor(address _link) public { + if(_link == address(0)) { + setPublicChainlinkToken(); + } else { + setChainlinkToken(_link); + } + } + + /** + * @notice Allows the owner of the contract to create new service agreements + * with multiple oracles. Each oracle will have their own Job ID and can have + * their own payment amount. + * @dev The globalNonce keeps service agreement IDs unique. Assume one cannot + * create the max uint256 number of service agreements in the same block. + * @param _minResponses The minimum number of responses before the requesting + * contract is called with the response data. + * @param _oracles The list of oracle contract addresses. + * @param _jobIds The corresponding list of Job IDs. + * @param _payments The corresponding list of payment amounts. + */ + function createServiceAgreement( + uint256 _minResponses, + address[] calldata _oracles, + bytes32[] calldata _jobIds, + uint256[] calldata _payments + ) + external returns (bytes32 saId) + { + require(_minResponses > 0, "Min responses must be > 0"); + require(_oracles.length == _jobIds.length && _oracles.length == _payments.length, "Unmet length"); + require(_oracles.length <= MAX_ORACLE_COUNT, "Cannot have more than 45 oracles"); + require(_oracles.length >= _minResponses, "Invalid min responses"); + uint256 totalPayment; + for (uint i = 0; i < _payments.length; i++) { + totalPayment = totalPayment.add(_payments[i]); + } + saId = keccak256(abi.encodePacked(globalNonce, now)); + globalNonce++; // yes, let it overflow + serviceAgreements[saId] = ServiceAgreement(totalPayment, _minResponses, _oracles, _jobIds, _payments); + + emit NewServiceAgreement(saId, totalPayment, _minResponses); + } + + /** + * @notice This is a helper function to retrieve the details of a service agreement + * by its given service agreement ID. + * @dev This function is used instead of the public mapping to return the values + * of the arrays: oracles, jobIds, and payments. + */ + function getServiceAgreement(bytes32 _saId) + external view returns + ( + uint256 totalPayment, + uint256 minResponses, + address[] memory oracles, + bytes32[] memory jobIds, + uint256[] memory payments + ) + { + return + ( + serviceAgreements[_saId].totalPayment, + serviceAgreements[_saId].minResponses, + serviceAgreements[_saId].oracles, + serviceAgreements[_saId].jobIds, + serviceAgreements[_saId].payments + ); + } + + /** + * @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 chainlinkTokenAddress(); + } + + /** + * @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 _saId 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 _data The CBOR payload of the request + */ + function oracleRequest( + address _sender, + uint256 _payment, + bytes32 _saId, + address _callbackAddress, + bytes4 _callbackFunctionId, + uint256 _nonce, + uint256, + bytes calldata _data + ) + external + onlyLINK + override + checkCallbackAddress(_callbackAddress) + { + uint256 totalPayment = serviceAgreements[_saId].totalPayment; + // this revert message does not bubble up + require(_payment >= totalPayment, "Insufficient payment"); + bytes32 callbackRequestId = keccak256(abi.encodePacked(_sender, _nonce)); + require(requesters[callbackRequestId].sender == address(0), "Nonce already in-use"); + requesters[callbackRequestId].callbackFunctionId = _callbackFunctionId; + requesters[callbackRequestId].callbackAddress = _callbackAddress; + requesters[callbackRequestId].sender = _sender; + createRequests(_saId, callbackRequestId, _data); + if (_payment > totalPayment) { + uint256 overage = _payment.sub(totalPayment); + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + assert(_link.transfer(_sender, overage)); + } + } + + /** + * @dev Creates Chainlink requests to each oracle in the service agreement with the + * same data payload supplied by the requester + * @param _saId The service agreement ID + * @param _incomingRequestId The requester-supplied request ID + * @param _data The data payload (request parameters) to send to each oracle + */ + function createRequests(bytes32 _saId, bytes32 _incomingRequestId, bytes memory _data) private { + ServiceAgreement memory sa = serviceAgreements[_saId]; + require(sa.minResponses > 0, "Invalid service agreement"); + Chainlink.Request memory request; + bytes32 outgoingRequestId; + emit ServiceAgreementRequested(_saId, _incomingRequestId, sa.totalPayment); + for (uint i = 0; i < sa.oracles.length; i++) { + request = buildChainlinkRequest(sa.jobIds[i], address(this), this.chainlinkCallback.selector); + request.setBuffer(_data); + outgoingRequestId = sendChainlinkRequestTo(sa.oracles[i], request, sa.payments[i]); + requests[outgoingRequestId] = _incomingRequestId; + serviceAgreementRequests[outgoingRequestId] = _saId; + } + } + + /** + * @notice The fulfill method from requests created by this contract + * @dev The recordChainlinkFulfillment protects this function from being called + * by anyone other than the oracle address that the request was sent to + * @param _requestId The ID that was generated for the request + * @param _data The answer provided by the oracle + */ + function chainlinkCallback(bytes32 _requestId, int256 _data) + external + recordChainlinkFulfillment(_requestId) + returns (bool) + { + ServiceAgreement memory sa = serviceAgreements[serviceAgreementRequests[_requestId]]; + bytes32 cbRequestId = requests[_requestId]; + bytes32 saId = serviceAgreementRequests[_requestId]; + delete requests[_requestId]; + delete serviceAgreementRequests[_requestId]; + emit ServiceAgreementResponseReceived(saId, cbRequestId, msg.sender, _data); + requesters[cbRequestId].responses.push(_data); + Requester memory req = requesters[cbRequestId]; + if (req.responses.length == sa.oracles.length) delete requesters[cbRequestId]; + bool success = true; + if (req.responses.length == sa.minResponses) { + int256 result = Median.calculate(req.responses); + emit ServiceAgreementAnswerUpdated(saId, cbRequestId, result); + // solhint-disable-next-line avoid-low-level-calls + (success, ) = req.callbackAddress.call(abi.encodeWithSelector(req.callbackFunctionId, cbRequestId, result)); + } + return success; + } + + /** + * @notice Allows the owner to withdraw any LINK balance on the contract + * @dev The only valid case for there to be remaining LINK on this contract + * is if a user accidentally sent LINK directly to this contract's address. + */ + function withdrawLink() external onlyOwner { + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer"); + } + + /** + * @notice Call this method if no response is received within 5 minutes + * @param _requestId The ID that was generated for the request to cancel + * @param _payment The payment specified for the request to cancel + * @param _callbackFunctionId The bytes4 callback function ID specified for + * the request to cancel + * @param _expiration The expiration generated for the request to cancel + */ + function cancelOracleRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) + external + override + { + bytes32 cbRequestId = requests[_requestId]; + delete requests[_requestId]; + delete serviceAgreementRequests[_requestId]; + Requester memory req = requesters[cbRequestId]; + require(req.sender == msg.sender, "Only requester can cancel"); + delete requesters[cbRequestId]; + cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(req.sender, _payment), "Unable to transfer"); + } + + /** + * @dev Reverts if the callback address is the LINK token + * @param _to The callback address + */ + modifier checkCallbackAddress(address _to) { + require(_to != chainlinkTokenAddress(), "Cannot callback to LINK"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath128.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath128.sol new file mode 100644 index 0000000..c79665b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath128.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * This library is a version of Open Zeppelin's SafeMath, modified to support + * unsigned 128 bit integers. + */ +library SafeMath128 { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint128 a, uint128 b) internal pure returns (uint128) { + uint128 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint128 a, uint128 b) internal pure returns (uint128) { + require(b <= a, "SafeMath: subtraction overflow"); + uint128 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint128 a, uint128 b) internal pure returns (uint128) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint128 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint128 a, uint128 b) internal pure returns (uint128) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint128 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint128 a, uint128 b) internal pure returns (uint128) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath32.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath32.sol new file mode 100644 index 0000000..21944bb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath32.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * This library is a version of Open Zeppelin's SafeMath, modified to support + * unsigned 32 bit integers. + */ +library SafeMath32 { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint32 a, uint32 b) internal pure returns (uint32) { + uint32 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint32 a, uint32 b) internal pure returns (uint32) { + require(b <= a, "SafeMath: subtraction overflow"); + uint32 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint32 a, uint32 b) internal pure returns (uint32) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint32 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint32 a, uint32 b) internal pure returns (uint32) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint32 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint32 a, uint32 b) internal pure returns (uint32) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath64.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath64.sol new file mode 100644 index 0000000..2bb3b79 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SafeMath64.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * This library is a version of Open Zeppelin's SafeMath, modified to support + * unsigned 64 bit integers. + */ +library SafeMath64 { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint64 a, uint64 b) internal pure returns (uint64) { + uint64 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint64 a, uint64 b) internal pure returns (uint64) { + require(b <= a, "SafeMath: subtraction overflow"); + uint64 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint64 a, uint64 b) internal pure returns (uint64) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint64 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint64 a, uint64 b) internal pure returns (uint64) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint64 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint64 a, uint64 b) internal pure returns (uint64) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/SignedSafeMath.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SignedSafeMath.sol new file mode 100644 index 0000000..32941de --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SignedSafeMath.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +library SignedSafeMath { + int256 constant private _INT256_MIN = -2**255; + + /** + * @dev Multiplies two signed integers, reverts on overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); + + int256 c = a * b; + require(c / a == b, "SignedSafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + require(b != 0, "SignedSafeMath: division by zero"); + require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); + + int256 c = a / b; + + return c; + } + + /** + * @dev Subtracts two signed integers, reverts on overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + int256 c = a - b; + require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); + + return c; + } + + /** + * @dev Adds two signed integers, reverts on overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + int256 c = a + b; + require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); + + return c; + } + + /** + * @notice Computes average of two signed integers, ensuring that the computation + * doesn't overflow. + * @dev If the result is not an integer, it is rounded towards zero. For example, + * avg(-3, -4) = -3 + */ + function avg(int256 _a, int256 _b) + internal + pure + returns (int256) + { + if ((_a < 0 && _b > 0) || (_a > 0 && _b < 0)) { + return add(_a, _b) / 2; + } + int256 remainder = (_a % 2 + _b % 2) / 2; + return add(add(_a / 2, _b / 2), remainder); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/SimpleReadAccessController.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SimpleReadAccessController.sol new file mode 100644 index 0000000..fbd714a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SimpleReadAccessController.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./SimpleWriteAccessController.sol"; + +/** + * @title SimpleReadAccessController + * @notice Gives access to: + * - any externally owned account (note that off-chain actors can always read + * any contract storage regardless of on-chain access control measures, so this + * does not weaken the access control while improving usability) + * - accounts explicitly added to an access list + * @dev SimpleReadAccessController is not suitable for access controlling writes + * since it grants any externally owned account access! See + * SimpleWriteAccessController for that. + */ +contract SimpleReadAccessController is SimpleWriteAccessController { + + /** + * @notice Returns the access of an address + * @param _user The address to query + */ + function hasAccess( + address _user, + bytes memory _calldata + ) + public + view + virtual + override + returns (bool) + { + return super.hasAccess(_user, _calldata) || _user == tx.origin; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/SimpleWriteAccessController.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SimpleWriteAccessController.sol new file mode 100644 index 0000000..02a6db7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/SimpleWriteAccessController.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.6.0 <0.8.0; + +import "./Owned.sol"; +import "./interfaces/AccessControllerInterface.sol"; + +/** + * @title SimpleWriteAccessController + * @notice Gives access to accounts explicitly added to an access list by the + * controller's owner. + * @dev does not make any special permissions for externally, see + * SimpleReadAccessController for that. + */ +contract SimpleWriteAccessController is AccessControllerInterface, Owned { + + bool public checkEnabled; + mapping(address => bool) internal accessList; + + event AddedAccess(address user); + event RemovedAccess(address user); + event CheckAccessEnabled(); + event CheckAccessDisabled(); + + constructor() + public + { + checkEnabled = true; + } + + /** + * @notice Returns the access of an address + * @param _user The address to query + */ + function hasAccess( + address _user, + bytes memory + ) + public + view + virtual + override + returns (bool) + { + return accessList[_user] || !checkEnabled; + } + + /** + * @notice Adds an address to the access list + * @param _user The address to add + */ + function addAccess(address _user) + external + onlyOwner() + { + if (!accessList[_user]) { + accessList[_user] = true; + + emit AddedAccess(_user); + } + } + + /** + * @notice Removes an address from the access list + * @param _user The address to remove + */ + function removeAccess(address _user) + external + onlyOwner() + { + if (accessList[_user]) { + accessList[_user] = false; + + emit RemovedAccess(_user); + } + } + + /** + * @notice makes the access check enforced + */ + function enableAccessCheck() + external + onlyOwner() + { + if (!checkEnabled) { + checkEnabled = true; + + emit CheckAccessEnabled(); + } + } + + /** + * @notice makes the access check unenforced + */ + function disableAccessCheck() + external + onlyOwner() + { + if (checkEnabled) { + checkEnabled = false; + + emit CheckAccessDisabled(); + } + } + + /** + * @dev reverts if the caller does not have access + */ + modifier checkAccess() { + require(hasAccess(msg.sender, msg.data), "No access"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRF.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRF.sol new file mode 100644 index 0000000..9cbb145 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRF.sol @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +/** **************************************************************************** + * @notice Verification of verifiable-random-function (VRF) proofs, following + * @notice https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @notice See https://eprint.iacr.org/2017/099.pdf for security proofs. + + * @dev Bibliographic references: + + * @dev Goldberg, et al., "Verifiable Random Functions (VRFs)", Internet Draft + * @dev draft-irtf-cfrg-vrf-05, IETF, Aug 11 2019, + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05 + + * @dev Papadopoulos, et al., "Making NSEC5 Practical for DNSSEC", Cryptology + * @dev ePrint Archive, Report 2017/099, https://eprint.iacr.org/2017/099.pdf + * **************************************************************************** + * @dev USAGE + + * @dev The main entry point is randomValueFromVRFProof. See its docstring. + * **************************************************************************** + * @dev PURPOSE + + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is computationally indistinguishable to her from a uniform + * @dev random sample from the output space. + + * @dev The purpose of this contract is to perform that verification. + * **************************************************************************** + * @dev DESIGN NOTES + + * @dev The VRF algorithm verified here satisfies the full unqiqueness, full + * @dev collision resistance, and full pseudorandomness security properties. + * @dev See "SECURITY PROPERTIES" below, and + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-3 + + * @dev An elliptic curve point is generally represented in the solidity code + * @dev as a uint256[2], corresponding to its affine coordinates in + * @dev GF(FIELD_SIZE). + + * @dev For the sake of efficiency, this implementation deviates from the spec + * @dev in some minor ways: + + * @dev - Keccak hash rather than the SHA256 hash recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev Keccak costs much less gas on the EVM, and provides similar security. + + * @dev - Secp256k1 curve instead of the P-256 or ED25519 curves recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev For curve-point multiplication, it's much cheaper to abuse ECRECOVER + + * @dev - hashToCurve recursively hashes until it finds a curve x-ordinate. On + * @dev the EVM, this is slightly more efficient than the recommendation in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + * @dev step 5, to concatenate with a nonce then hash, and rehash with the + * @dev nonce updated until a valid x-ordinate is found. + + * @dev - hashToCurve does not include a cipher version string or the byte 0x1 + * @dev in the hash message, as recommended in step 5.B of the draft + * @dev standard. They are unnecessary here because no variation in the + * @dev cipher suite is allowed. + + * @dev - Similarly, the hash input in scalarFromCurvePoints does not include a + * @dev commitment to the cipher suite, either, which differs from step 2 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + * @dev . Also, the hash input is the concatenation of the uncompressed + * @dev points, not the compressed points as recommended in step 3. + + * @dev - In the calculation of the challenge value "c", the "u" value (i.e. + * @dev the value computed by Reggie as the nonce times the secp256k1 + * @dev generator point, see steps 5 and 7 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @dev ) is replaced by its ethereum address, i.e. the lower 160 bits of the + * @dev keccak hash of the original u. This is because we only verify the + * @dev calculation of u up to its address, by abusing ECRECOVER. + * **************************************************************************** + * @dev SECURITY PROPERTIES + + * @dev Here are the security properties for this VRF: + + * @dev Full uniqueness: For any seed and valid VRF public key, there is + * @dev exactly one VRF output which can be proved to come from that seed, in + * @dev the sense that the proof will pass verifyVRFProof. + + * @dev Full collision resistance: It's cryptographically infeasible to find + * @dev two seeds with same VRF output from a fixed, valid VRF key + + * @dev Full pseudorandomness: Absent the proofs that the VRF outputs are + * @dev derived from a given seed, the outputs are computationally + * @dev indistinguishable from randomness. + + * @dev https://eprint.iacr.org/2017/099.pdf, Appendix B contains the proofs + * @dev for these properties. + + * @dev For secp256k1, the key validation described in section + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.6 + * @dev is unnecessary, because secp256k1 has cofactor 1, and the + * @dev representation of the public key used here (affine x- and y-ordinates + * @dev of the secp256k1 point on the standard y^2=x^3+7 curve) cannot refer to + * @dev the point at infinity. + * **************************************************************************** + * @dev OTHER SECURITY CONSIDERATIONS + * + * @dev The seed input to the VRF could in principle force an arbitrary amount + * @dev of work in hashToCurve, by requiring extra rounds of hashing and + * @dev checking whether that's yielded the x ordinate of a secp256k1 point. + * @dev However, under the Random Oracle Model the probability of choosing a + * @dev point which forces n extra rounds in hashToCurve is 2⁻ⁿ. The base cost + * @dev for calling hashToCurve is about 25,000 gas, and each round of checking + * @dev for a valid x ordinate costs about 15,555 gas, so to find a seed for + * @dev which hashToCurve would cost more than 2,017,000 gas, one would have to + * @dev try, in expectation, about 2¹²⁸ seeds, which is infeasible for any + * @dev foreseeable computational resources. (25,000 + 128 * 15,555 < 2,017,000.) + + * @dev Since the gas block limit for the Ethereum main net is 10,000,000 gas, + * @dev this means it is infeasible for an adversary to prevent correct + * @dev operation of this contract by choosing an adverse seed. + + * @dev (See TestMeasureHashToCurveGasCost for verification of the gas cost for + * @dev hashToCurve.) + + * @dev It may be possible to make a secure constant-time hashToCurve function. + * @dev See notes in hashToCurve docstring. +*/ +contract VRF { + + // See https://www.secg.org/sec2-v2.pdf, section 2.4.1, for these constants. + uint256 constant private GROUP_ORDER = // Number of points in Secp256k1 + // solium-disable-next-line indentation + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + // Prime characteristic of the galois field over which Secp256k1 is defined + uint256 constant private FIELD_SIZE = + // solium-disable-next-line indentation + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; + uint256 constant private WORD_LENGTH_BYTES = 0x20; + + // (base^exponent) % FIELD_SIZE + // Cribbed from https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 + function bigModExp(uint256 base, uint256 exponent) + internal view returns (uint256 exponentiation) { + uint256 callResult; + uint256[6] memory bigModExpContractInputs; + bigModExpContractInputs[0] = WORD_LENGTH_BYTES; // Length of base + bigModExpContractInputs[1] = WORD_LENGTH_BYTES; // Length of exponent + bigModExpContractInputs[2] = WORD_LENGTH_BYTES; // Length of modulus + bigModExpContractInputs[3] = base; + bigModExpContractInputs[4] = exponent; + bigModExpContractInputs[5] = FIELD_SIZE; + uint256[1] memory output; + assembly { // solhint-disable-line no-inline-assembly + callResult := staticcall( + not(0), // Gas cost: no limit + 0x05, // Bigmodexp contract address + bigModExpContractInputs, + 0xc0, // Length of input segment: 6*0x20-bytes + output, + 0x20 // Length of output segment + ) + } + if (callResult == 0) {revert("bigModExp failure!");} + return output[0]; + } + + // Let q=FIELD_SIZE. q % 4 = 3, ∴ x≡r^2 mod q ⇒ x^SQRT_POWER≡±r mod q. See + // https://en.wikipedia.org/wiki/Modular_square_root#Prime_or_prime_power_modulus + uint256 constant private SQRT_POWER = (FIELD_SIZE + 1) >> 2; + + // Computes a s.t. a^2 = x in the field. Assumes a exists + function squareRoot(uint256 x) internal view returns (uint256) { + return bigModExp(x, SQRT_POWER); + } + + // The value of y^2 given that (x,y) is on secp256k1. + function ySquared(uint256 x) internal pure returns (uint256) { + // Curve is y^2=x^3+7. See section 2.4.1 of https://www.secg.org/sec2-v2.pdf + uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE); + return addmod(xCubed, 7, FIELD_SIZE); + } + + // True iff p is on secp256k1 + function isOnCurve(uint256[2] memory p) internal pure returns (bool) { + return ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE); + } + + // Hash x uniformly into {0, ..., FIELD_SIZE-1}. + function fieldHash(bytes memory b) internal pure returns (uint256 x_) { + x_ = uint256(keccak256(b)); + // Rejecting if x >= FIELD_SIZE corresponds to step 2.1 in section 2.3.4 of + // http://www.secg.org/sec1-v2.pdf , which is part of the definition of + // string_to_point in the IETF draft + while (x_ >= FIELD_SIZE) { + x_ = uint256(keccak256(abi.encodePacked(x_))); + } + } + + // Hash b to a random point which hopefully lies on secp256k1. The y ordinate + // is always even, due to + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + // step 5.C, which references arbitrary_string_to_point, defined in + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 as + // returning the point with given x ordinate, and even y ordinate. + function newCandidateSecp256k1Point(bytes memory b) + internal view returns (uint256[2] memory p) { + p[0] = fieldHash(b); + p[1] = squareRoot(ySquared(p[0])); + if (p[1] % 2 == 1) { + p[1] = FIELD_SIZE - p[1]; + } + } + + // Domain-separation tag for initial hash in hashToCurve. Corresponds to + // vrf.go/hashToCurveHashPrefix + uint256 constant HASH_TO_CURVE_HASH_PREFIX = 1; + + // Cryptographic hash function onto the curve. + // + // Corresponds to algorithm in section 5.4.1.1 of the draft standard. (But see + // DESIGN NOTES above for slight differences.) + // + // TODO(alx): Implement a bounded-computation hash-to-curve, as described in + // "Construction of Rational Points on Elliptic Curves over Finite Fields" + // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.831.5299&rep=rep1&type=pdf + // and suggested by + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-01#section-5.2.2 + // (Though we can't used exactly that because secp256k1's j-invariant is 0.) + // + // This would greatly simplify the analysis in "OTHER SECURITY CONSIDERATIONS" + // https://www.pivotaltracker.com/story/show/171120900 + function hashToCurve(uint256[2] memory pk, uint256 input) + internal view returns (uint256[2] memory rv) { + rv = newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, + pk, input)); + while (!isOnCurve(rv)) { + rv = newCandidateSecp256k1Point(abi.encodePacked(rv[0])); + } + } + + /** ********************************************************************* + * @notice Check that product==scalar*multiplicand + * + * @dev Based on Vitalik Buterin's idea in ethresear.ch post cited below. + * + * @param multiplicand: secp256k1 point + * @param scalar: non-zero GF(GROUP_ORDER) scalar + * @param product: secp256k1 expected to be multiplier * multiplicand + * @return verifies true iff product==scalar*multiplicand, with cryptographically high probability + */ + function ecmulVerify(uint256[2] memory multiplicand, uint256 scalar, + uint256[2] memory product) internal pure returns(bool verifies) + { + require(scalar != 0); // Rules out an ecrecover failure case + uint256 x = multiplicand[0]; // x ordinate of multiplicand + uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28; // parity of y ordinate + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // Point corresponding to address ecrecover(0, v, x, s=scalar*x) is + // (x⁻¹ mod GROUP_ORDER) * (scalar * x * multiplicand - 0 * g), i.e. + // scalar*multiplicand. See https://crypto.stackexchange.com/a/18106 + bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER)); + address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX); + // Explicit conversion to address takes bottom 160 bits + address expected = address(uint256(keccak256(abi.encodePacked(product)))); + return (actual == expected); + } + + // Returns x1/z1-x2/z2=(x1z2-x2z1)/(z1z2) in projective coordinates on P¹(𝔽ₙ) + function projectiveSub(uint256 x1, uint256 z1, uint256 x2, uint256 z2) + internal pure returns(uint256 x3, uint256 z3) { + uint256 num1 = mulmod(z2, x1, FIELD_SIZE); + uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); + (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + + // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) + function projectiveMul(uint256 x1, uint256 z1, uint256 x2, uint256 z2) + internal pure returns(uint256 x3, uint256 z3) { + (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + + /** ************************************************************************** + @notice Computes elliptic-curve sum, in projective co-ordinates + + @dev Using projective coordinates avoids costly divisions + + @dev To use this with p and q in affine coordinates, call + @dev projectiveECAdd(px, py, qx, qy). This will return + @dev the addition of (px, py, 1) and (qx, qy, 1), in the + @dev secp256k1 group. + + @dev This can be used to calculate the z which is the inverse to zInv + @dev in isValidVRFOutput. But consider using a faster + @dev re-implementation such as ProjectiveECAdd in the golang vrf package. + + @dev This function assumes [px,py,1],[qx,qy,1] are valid projective + coordinates of secp256k1 points. That is safe in this contract, + because this method is only used by linearCombination, which checks + points are on the curve via ecrecover. + ************************************************************************** + @param px The first affine coordinate of the first summand + @param py The second affine coordinate of the first summand + @param qx The first affine coordinate of the second summand + @param qy The second affine coordinate of the second summand + + (px,py) and (qx,qy) must be distinct, valid secp256k1 points. + ************************************************************************** + Return values are projective coordinates of [px,py,1]+[qx,qy,1] as points + on secp256k1, in P²(𝔽ₙ) + @return sx + @return sy + @return sz + */ + function projectiveECAdd(uint256 px, uint256 py, uint256 qx, uint256 qy) + internal pure returns(uint256 sx, uint256 sy, uint256 sz) { + // See "Group law for E/K : y^2 = x^3 + ax + b", in section 3.1.2, p. 80, + // "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes and Vanstone + // We take the equations there for (sx,sy), and homogenize them to + // projective coordinates. That way, no inverses are required, here, and we + // only need the one inverse in affineECAdd. + + // We only need the "point addition" equations from Hankerson et al. Can + // skip the "point doubling" equations because p1 == p2 is cryptographically + // impossible, and require'd not to be the case in linearCombination. + + // Add extra "projective coordinate" to the two points + (uint256 z1, uint256 z2) = (1, 1); + + // (lx, lz) = (qy-py)/(qx-px), i.e., gradient of secant line. + uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE); + uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE); + + uint256 dx; // Accumulates denominator from sx calculation + // sx=((qy-py)/(qx-px))^2-px-qx + (sx, dx) = projectiveMul(lx, lz, lx, lz); // ((qy-py)/(qx-px))^2 + (sx, dx) = projectiveSub(sx, dx, px, z1); // ((qy-py)/(qx-px))^2-px + (sx, dx) = projectiveSub(sx, dx, qx, z2); // ((qy-py)/(qx-px))^2-px-qx + + uint256 dy; // Accumulates denominator from sy calculation + // sy=((qy-py)/(qx-px))(px-sx)-py + (sy, dy) = projectiveSub(px, z1, sx, dx); // px-sx + (sy, dy) = projectiveMul(sy, dy, lx, lz); // ((qy-py)/(qx-px))(px-sx) + (sy, dy) = projectiveSub(sy, dy, py, z1); // ((qy-py)/(qx-px))(px-sx)-py + + if (dx != dy) { // Cross-multiply to put everything over a common denominator + sx = mulmod(sx, dy, FIELD_SIZE); + sy = mulmod(sy, dx, FIELD_SIZE); + sz = mulmod(dx, dy, FIELD_SIZE); + } else { // Already over a common denominator, use that for z ordinate + sz = dx; + } + } + + // p1+p2, as affine points on secp256k1. + // + // invZ must be the inverse of the z returned by projectiveECAdd(p1, p2). + // It is computed off-chain to save gas. + // + // p1 and p2 must be distinct, because projectiveECAdd doesn't handle + // point doubling. + function affineECAdd( + uint256[2] memory p1, uint256[2] memory p2, + uint256 invZ) internal pure returns (uint256[2] memory) { + uint256 x; + uint256 y; + uint256 z; + (x, y, z) = projectiveECAdd(p1[0], p1[1], p2[0], p2[1]); + require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z"); + // Clear the z ordinate of the projective representation by dividing through + // by it, to obtain the affine representation + return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)]; + } + + // True iff address(c*p+s*g) == lcWitness, where g is generator. (With + // cryptographically high probability.) + function verifyLinearCombinationWithGenerator( + uint256 c, uint256[2] memory p, uint256 s, address lcWitness) + internal pure returns (bool) { + // Rule out ecrecover failure modes which return address 0. + require(lcWitness != address(0), "bad witness"); + uint8 v = (p[1] % 2 == 0) ? 27 : 28; // parity of y-ordinate of p + bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER)); // -s*p[0] + bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER)); // c*p[0] + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // The point corresponding to the address returned by + // ecrecover(-s*p[0],v,p[0],c*p[0]) is + // (p[0]⁻¹ mod GROUP_ORDER)*(c*p[0]-(-s)*p[0]*g)=c*p+s*g. + // See https://crypto.stackexchange.com/a/18106 + // https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v + address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature); + return computed == lcWitness; + } + + // c*p1 + s*p2. Requires cp1Witness=c*p1 and sp2Witness=s*p2. Also + // requires cp1Witness != sp2Witness (which is fine for this application, + // since it is cryptographically impossible for them to be equal. In the + // (cryptographically impossible) case that a prover accidentally derives + // a proof with equal c*p1 and s*p2, they should retry with a different + // proof nonce.) Assumes that all points are on secp256k1 + // (which is checked in verifyVRFProof below.) + function linearCombination( + uint256 c, uint256[2] memory p1, uint256[2] memory cp1Witness, + uint256 s, uint256[2] memory p2, uint256[2] memory sp2Witness, + uint256 zInv) + internal pure returns (uint256[2] memory) { + require((cp1Witness[0] - sp2Witness[0]) % FIELD_SIZE != 0, + "points in sum must be distinct"); + require(ecmulVerify(p1, c, cp1Witness), "First multiplication check failed"); + require(ecmulVerify(p2, s, sp2Witness), "Second multiplication check failed"); + return affineECAdd(cp1Witness, sp2Witness, zInv); + } + + // Domain-separation tag for the hash taken in scalarFromCurvePoints. + // Corresponds to scalarFromCurveHashPrefix in vrf.go + uint256 constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2; + + // Pseudo-random number from inputs. Matches vrf.go/scalarFromCurvePoints, and + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + // The draft calls (in step 7, via the definition of string_to_int, in + // https://datatracker.ietf.org/doc/html/rfc8017#section-4.2 ) for taking the + // first hash without checking that it corresponds to a number less than the + // group order, which will lead to a slight bias in the sample. + // + // TODO(alx): We could save a bit of gas by following the standard here and + // using the compressed representation of the points, if we collated the y + // parities into a single bytes32. + // https://www.pivotaltracker.com/story/show/171120588 + function scalarFromCurvePoints( + uint256[2] memory hash, uint256[2] memory pk, uint256[2] memory gamma, + address uWitness, uint256[2] memory v) + internal pure returns (uint256 s) { + return uint256( + keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, + hash, pk, gamma, v, uWitness))); + } + + // True if (gamma, c, s) is a correctly constructed randomness proof from pk + // and seed. zInv must be the inverse of the third ordinate from + // projectiveECAdd applied to cGammaWitness and sHashWitness. Corresponds to + // section 5.3 of the IETF draft. + // + // TODO(alx): Since I'm only using pk in the ecrecover call, I could only pass + // the x ordinate, and the parity of the y ordinate in the top bit of uWitness + // (which I could make a uint256 without using any extra space.) Would save + // about 2000 gas. https://www.pivotaltracker.com/story/show/170828567 + function verifyVRFProof( + uint256[2] memory pk, uint256[2] memory gamma, uint256 c, uint256 s, + uint256 seed, address uWitness, uint256[2] memory cGammaWitness, + uint256[2] memory sHashWitness, uint256 zInv) + internal view { + require(isOnCurve(pk), "public key is not on curve"); + require(isOnCurve(gamma), "gamma is not on curve"); + require(isOnCurve(cGammaWitness), "cGammaWitness is not on curve"); + require(isOnCurve(sHashWitness), "sHashWitness is not on curve"); + // Step 5. of IETF draft section 5.3 (pk corresponds to 5.3's Y, and here + // we use the address of u instead of u itself. Also, here we add the + // terms instead of taking the difference, and in the proof consruction in + // vrf.GenerateProof, we correspondingly take the difference instead of + // taking the sum as they do in step 7 of section 5.1.) + require( + verifyLinearCombinationWithGenerator(c, pk, s, uWitness), + "addr(c*pk+s*g)≠_uWitness" + ); + // Step 4. of IETF draft section 5.3 (pk corresponds to Y, seed to alpha_string) + uint256[2] memory hash = hashToCurve(pk, seed); + // Step 6. of IETF draft section 5.3, but see note for step 5 about +/- terms + uint256[2] memory v = linearCombination( + c, gamma, cGammaWitness, s, hash, sHashWitness, zInv); + // Steps 7. and 8. of IETF draft section 5.3 + uint256 derivedC = scalarFromCurvePoints(hash, pk, gamma, uWitness, v); + require(c == derivedC, "invalid proof"); + } + + // Domain-separation tag for the hash used as the final VRF output. + // Corresponds to vrfRandomOutputHashPrefix in vrf.go + uint256 constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3; + + // Length of proof marshaled to bytes array. Shows layout of proof + uint public constant PROOF_LENGTH = 64 + // PublicKey (uncompressed format.) + 64 + // Gamma + 32 + // C + 32 + // S + 32 + // Seed + 0 + // Dummy entry: The following elements are included for gas efficiency: + 32 + // uWitness (gets padded to 256 bits, even though it's only 160) + 64 + // cGammaWitness + 64 + // sHashWitness + 32; // zInv (Leave Output out, because that can be efficiently calculated) + + /* *************************************************************************** + * @notice Returns proof's output, if proof is valid. Otherwise reverts + + * @param proof A binary-encoded proof, as output by vrf.Proof.MarshalForSolidityVerifier + * + * Throws if proof is invalid, otherwise: + * @return output i.e., the random output implied by the proof + * *************************************************************************** + * @dev See the calculation of PROOF_LENGTH for the binary layout of proof. + */ + function randomValueFromVRFProof(bytes memory proof) + internal view returns (uint256 output) { + require(proof.length == PROOF_LENGTH, "wrong proof length"); + + uint256[2] memory pk; // parse proof contents into these variables + uint256[2] memory gamma; + // c, s and seed combined (prevents "stack too deep" compilation error) + uint256[3] memory cSSeed; + address uWitness; + uint256[2] memory cGammaWitness; + uint256[2] memory sHashWitness; + uint256 zInv; + (pk, gamma, cSSeed, uWitness, cGammaWitness, sHashWitness, zInv) = abi.decode( + proof, (uint256[2], uint256[2], uint256[3], address, uint256[2], + uint256[2], uint256)); + verifyVRFProof( + pk, + gamma, + cSSeed[0], // c + cSSeed[1], // s + cSSeed[2], // seed + uWitness, + cGammaWitness, + sHashWitness, + zInv + ); + output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, gamma))); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFConsumerBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFConsumerBase.sol new file mode 100644 index 0000000..ca1c381 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFConsumerBase.sol @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./vendor/SafeMathChainlink.sol"; + +import "./interfaces/LinkTokenInterface.sol"; + +import "./VRFRequestIDBase.sol"; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constructor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator, _link) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash), and have told you the minimum LINK + * @dev price for VRF service. Make sure your contract has sufficient LINK, and + * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you + * @dev want to generate randomness from. + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomness method. + * + * @dev The randomness argument to fulfillRandomness is the actual random value + * @dev generated from your seed. + * + * @dev The requestId argument is generated from the keyHash and the seed by + * @dev makeRequestId(keyHash, seed). If your contract could have concurrent + * @dev requests open, you can use the requestId to track which seed is + * @dev associated with which randomness. See VRFRequestIDBase.sol for more + * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously.) + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. (Which is critical to making unpredictable randomness! See the + * @dev next section.) + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the ultimate input to the VRF is mixed with the block hash of the + * @dev block in which the request is made, user-provided seeds have no impact + * @dev on its economic security properties. They are only included for API + * @dev compatability with previous versions of this contract. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. + */ +abstract contract VRFConsumerBase is VRFRequestIDBase { + + using SafeMathChainlink for uint256; + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBase expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomness the VRF output + */ + function fulfillRandomness(bytes32 requestId, uint256 randomness) + internal virtual; + + /** + * @dev In order to keep backwards compatibility we have kept the user + * seed field around. We remove the use of it because given that the blockhash + * enters later, it overrides whatever randomness the used seed provides. + * Given that it adds no security, and can easily lead to misunderstandings, + * we have removed it from usage and can now provide a simpler API. + */ + uint256 constant private USER_SEED_PLACEHOLDER = 0; + + /** + * @notice requestRandomness initiates a request for VRF output given _seed + * + * @dev The fulfillRandomness method receives the output, once it's provided + * @dev by the Oracle, and verified by the vrfCoordinator. + * + * @dev The _keyHash must already be registered with the VRFCoordinator, and + * @dev the _fee must exceed the fee specified during registration of the + * @dev _keyHash. + * + * @dev The _seed parameter is vestigial, and is kept only for API + * @dev compatibility with older versions. It can't *hurt* to mix in some of + * @dev your own randomness, here, but it's not necessary because the VRF + * @dev oracle will mix the hash of the block containing your request into the + * @dev VRF seed it ultimately uses. + * + * @param _keyHash ID of public key against which randomness is generated + * @param _fee The amount of LINK to send with the request + * + * @return requestId unique ID for this request + * + * @dev The returned requestId can be used to distinguish responses to + * @dev concurrent requests. It is passed as the first argument to + * @dev fulfillRandomness. + */ + function requestRandomness(bytes32 _keyHash, uint256 _fee) + internal returns (bytes32 requestId) + { + LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); + // This is the seed passed to VRFCoordinator. The oracle will mix this with + // the hash of the block containing this request to obtain the seed/input + // which is finally passed to the VRF cryptographic machinery. + uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); + // nonces[_keyHash] must stay in sync with + // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above + // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). + // This provides protection against the user repeating their input seed, + // which would result in a predictable/duplicate output, if multiple such + // requests appeared in the same block. + nonces[_keyHash] = nonces[_keyHash].add(1); + return makeRequestId(_keyHash, vRFSeed); + } + + LinkTokenInterface immutable internal LINK; + address immutable private vrfCoordinator; + + // Nonces for each VRF key from which randomness has been requested. + // + // Must stay in sync with VRFCoordinator[_keyHash][this] + mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + * @param _link address of LINK token contract + * + * @dev https://docs.chain.link/docs/link-token-contracts + */ + constructor(address _vrfCoordinator, address _link) public { + vrfCoordinator = _vrfCoordinator; + LINK = LinkTokenInterface(_link); + } + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external { + require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); + fulfillRandomness(requestId, randomness); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFCoordinator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFCoordinator.sol new file mode 100644 index 0000000..9d1bcaf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFCoordinator.sol @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "./vendor/SafeMathChainlink.sol"; + +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/BlockHashStoreInterface.sol"; + +import "./vendor/Ownable.sol"; + +import "./VRF.sol"; +import "./VRFRequestIDBase.sol"; +import "./VRFConsumerBase.sol"; + +/** + * @title VRFCoordinator coordinates on-chain verifiable-randomness requests + * @title with off-chain responses + */ +contract VRFCoordinator is VRF, VRFRequestIDBase, Ownable { + + using SafeMathChainlink for uint256; + + LinkTokenInterface internal LINK; + BlockHashStoreInterface internal blockHashStore; + + constructor(address _link, address _blockHashStore) public { + LINK = LinkTokenInterface(_link); + blockHashStore = BlockHashStoreInterface(_blockHashStore); + } + + struct Callback { // Tracks an ongoing request + address callbackContract; // Requesting contract, which will receive response + // Amount of LINK paid at request time. Total LINK = 1e9 * 1e18 < 2^96, so + // this representation is adequate, and saves a word of storage when this + // field follows the 160-bit callbackContract address. + uint96 randomnessFee; + // Commitment to seed passed to oracle by this contract, and the number of + // the block in which the request appeared. This is the keccak256 of the + // concatenation of those values. Storing this commitment saves a word of + // storage. + bytes32 seedAndBlockNum; + } + + struct ServiceAgreement { // Tracks oracle commitments to VRF service + address vRFOracle; // Oracle committing to respond with VRF service + uint96 fee; // Minimum payment for oracle response. Total LINK=1e9*1e18<2^96 + bytes32 jobID; // ID of corresponding chainlink job in oracle's DB + } + + mapping(bytes32 /* (provingKey, seed) */ => Callback) public callbacks; + mapping(bytes32 /* provingKey */ => ServiceAgreement) + public serviceAgreements; + mapping(address /* oracle */ => uint256 /* LINK balance */) + public withdrawableTokens; + mapping(bytes32 /* provingKey */ => mapping(address /* consumer */ => uint256)) + private nonces; + + // The oracle only needs the jobID to look up the VRF, but specifying public + // key as well prevents a malicious oracle from inducing VRF outputs from + // another oracle by reusing the jobID. + event RandomnessRequest( + bytes32 keyHash, + uint256 seed, + bytes32 indexed jobID, + address sender, + uint256 fee, + bytes32 requestID); + + event NewServiceAgreement(bytes32 keyHash, uint256 fee); + + event RandomnessRequestFulfilled(bytes32 requestId, uint256 output); + + /** + * @notice Commits calling address to serve randomness + * @param _fee minimum LINK payment required to serve randomness + * @param _oracle the address of the Chainlink node with the proving key and job + * @param _publicProvingKey public key used to prove randomness + * @param _jobID ID of the corresponding chainlink job in the oracle's db + */ + function registerProvingKey( + uint256 _fee, address _oracle, uint256[2] calldata _publicProvingKey, bytes32 _jobID + ) + external + onlyOwner() + { + bytes32 keyHash = hashOfKey(_publicProvingKey); + address oldVRFOracle = serviceAgreements[keyHash].vRFOracle; + require(oldVRFOracle == address(0), "please register a new key"); + require(_oracle != address(0), "_oracle must not be 0x0"); + serviceAgreements[keyHash].vRFOracle = _oracle; + serviceAgreements[keyHash].jobID = _jobID; + // Yes, this revert message doesn't fit in a word + require(_fee <= 1e9 ether, + "you can't charge more than all the LINK in the world, greedy"); + serviceAgreements[keyHash].fee = uint96(_fee); + emit NewServiceAgreement(keyHash, _fee); + } + + /** + * @notice Called by LINK.transferAndCall, on successful LINK transfer + * + * @dev To invoke this, use the requestRandomness method in VRFConsumerBase. + * + * @dev The VRFCoordinator will call back to the calling contract when the + * @dev oracle responds, on the method fulfillRandomness. See + * @dev VRFConsumerBase.fulfilRandomness for its signature. Your consuming + * @dev contract should inherit from VRFConsumerBase, and implement + * @dev fulfilRandomness. + * + * @param _sender address: who sent the LINK (must be a contract) + * @param _fee amount of LINK sent + * @param _data abi-encoded call to randomnessRequest + */ + function onTokenTransfer(address _sender, uint256 _fee, bytes memory _data) + public + onlyLINK + { + (bytes32 keyHash, uint256 seed) = abi.decode(_data, (bytes32, uint256)); + randomnessRequest(keyHash, seed, _fee, _sender); + } + + /** + * @notice creates the chainlink request for randomness + * + * @param _keyHash ID of the VRF public key against which to generate output + * @param _consumerSeed Input to the VRF, from which randomness is generated + * @param _feePaid Amount of LINK sent with request. Must exceed fee for key + * @param _sender Requesting contract; to be called back with VRF output + * + * @dev _consumerSeed is mixed with key hash, sender address and nonce to + * @dev obtain preSeed, which is passed to VRF oracle, which mixes it with the + * @dev hash of the block containing this request, to compute the final seed. + * + * @dev The requestId used to store the request data is constructed from the + * @dev preSeed and keyHash. + */ + function randomnessRequest( + bytes32 _keyHash, + uint256 _consumerSeed, + uint256 _feePaid, + address _sender + ) + internal + sufficientLINK(_feePaid, _keyHash) + { + uint256 nonce = nonces[_keyHash][_sender]; + uint256 preSeed = makeVRFInputSeed(_keyHash, _consumerSeed, _sender, nonce); + bytes32 requestId = makeRequestId(_keyHash, preSeed); + // Cryptographically guaranteed by preSeed including an increasing nonce + assert(callbacks[requestId].callbackContract == address(0)); + callbacks[requestId].callbackContract = _sender; + assert(_feePaid < 1e27); // Total LINK fits in uint96 + callbacks[requestId].randomnessFee = uint96(_feePaid); + callbacks[requestId].seedAndBlockNum = keccak256(abi.encodePacked( + preSeed, block.number)); + emit RandomnessRequest(_keyHash, preSeed, serviceAgreements[_keyHash].jobID, + _sender, _feePaid, requestId); + nonces[_keyHash][_sender] = nonces[_keyHash][_sender].add(1); + } + + // Offsets into fulfillRandomnessRequest's _proof of various values + // + // Public key. Skips byte array's length prefix. + uint256 public constant PUBLIC_KEY_OFFSET = 0x20; + // Seed is 7th word in proof, plus word for length, (6+1)*0x20=0xe0 + uint256 public constant PRESEED_OFFSET = 0xe0; + + /** + * @notice Called by the chainlink node to fulfill requests + * + * @param _proof the proof of randomness. Actual random output built from this + * + * @dev The structure of _proof corresponds to vrf.MarshaledOnChainResponse, + * @dev in the node source code. I.e., it is a vrf.MarshaledProof with the + * @dev seed replaced by the preSeed, followed by the hash of the requesting + * @dev block. + */ + function fulfillRandomnessRequest(bytes memory _proof) public { + (bytes32 currentKeyHash, Callback memory callback, bytes32 requestId, + uint256 randomness) = getRandomnessFromProof(_proof); + + // Pay oracle + address oadd = serviceAgreements[currentKeyHash].vRFOracle; + withdrawableTokens[oadd] = withdrawableTokens[oadd].add( + callback.randomnessFee); + + // Forget request. Must precede callback (prevents reentrancy) + delete callbacks[requestId]; + callBackWithRandomness(requestId, randomness, callback.callbackContract); + + emit RandomnessRequestFulfilled(requestId, randomness); + } + + function callBackWithRandomness(bytes32 requestId, uint256 randomness, + address consumerContract) internal { + // Dummy variable; allows access to method selector in next line. See + // https://github.com/ethereum/solidity/issues/3506#issuecomment-553727797 + VRFConsumerBase v; + bytes memory resp = abi.encodeWithSelector( + v.rawFulfillRandomness.selector, requestId, randomness); + // The bound b here comes from https://eips.ethereum.org/EIPS/eip-150. The + // actual gas available to the consuming contract will be b-floor(b/64). + // This is chosen to leave the consuming contract ~200k gas, after the cost + // of the call itself. + uint256 b = 206000; + require(gasleft() >= b, "not enough gas for consumer"); + // A low-level call is necessary, here, because we don't want the consuming + // contract to be able to revert this execution, and thus deny the oracle + // payment for a valid randomness response. This also necessitates the above + // check on the gasleft, as otherwise there would be no indication if the + // callback method ran out of gas. + // + // solhint-disable-next-line avoid-low-level-calls + (bool success,) = consumerContract.call(resp); + // Avoid unused-local-variable warning. (success is only present to prevent + // a warning that the return value of consumerContract.call is unused.) + (success); + } + + function getRandomnessFromProof(bytes memory _proof) + internal view returns (bytes32 currentKeyHash, Callback memory callback, + bytes32 requestId, uint256 randomness) { + // blockNum follows proof, which follows length word (only direct-number + // constants are allowed in assembly, so have to compute this in code) + uint256 BLOCKNUM_OFFSET = 0x20 + PROOF_LENGTH; + // _proof.length skips the initial length word, so not including the + // blocknum in this length check balances out. + require(_proof.length == BLOCKNUM_OFFSET, "wrong proof length"); + uint256[2] memory publicKey; + uint256 preSeed; + uint256 blockNum; + assembly { // solhint-disable-line no-inline-assembly + publicKey := add(_proof, PUBLIC_KEY_OFFSET) + preSeed := mload(add(_proof, PRESEED_OFFSET)) + blockNum := mload(add(_proof, BLOCKNUM_OFFSET)) + } + currentKeyHash = hashOfKey(publicKey); + requestId = makeRequestId(currentKeyHash, preSeed); + callback = callbacks[requestId]; + require(callback.callbackContract != address(0), "no corresponding request"); + require(callback.seedAndBlockNum == keccak256(abi.encodePacked(preSeed, + blockNum)), "wrong preSeed or block num"); + + bytes32 blockHash = blockhash(blockNum); + if (blockHash == bytes32(0)) { + blockHash = blockHashStore.getBlockhash(blockNum); + require(blockHash != bytes32(0), "please prove blockhash"); + } + // The seed actually used by the VRF machinery, mixing in the blockhash + uint256 actualSeed = uint256(keccak256(abi.encodePacked(preSeed, blockHash))); + // solhint-disable-next-line no-inline-assembly + assembly { // Construct the actual proof from the remains of _proof + mstore(add(_proof, PRESEED_OFFSET), actualSeed) + mstore(_proof, PROOF_LENGTH) + } + randomness = VRF.randomValueFromVRFProof(_proof); // Reverts on failure + } + + /** + * @dev Allows the oracle operator to withdraw their LINK + * @param _recipient is the address the funds will be sent to + * @param _amount is the amount of LINK transferred from the Coordinator contract + */ + function withdraw(address _recipient, uint256 _amount) + external + hasAvailableFunds(_amount) + { + withdrawableTokens[msg.sender] = withdrawableTokens[msg.sender].sub(_amount); + assert(LINK.transfer(_recipient, _amount)); + } + + /** + * @notice Returns the serviceAgreements key associated with this public key + * @param _publicKey the key to return the address for + */ + function hashOfKey(uint256[2] memory _publicKey) public pure returns (bytes32) { + return keccak256(abi.encodePacked(_publicKey)); + } + + /** + * @dev Reverts if amount is not at least what was agreed upon in the service agreement + * @param _feePaid The payment for the request + * @param _keyHash The key which the request is for + */ + modifier sufficientLINK(uint256 _feePaid, bytes32 _keyHash) { + require(_feePaid >= serviceAgreements[_keyHash].fee, "Below agreed payment"); + _; + } + +/** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + require(msg.sender == address(LINK), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if amount requested is greater than withdrawable balance + * @param _amount The given amount to compare to `withdrawableTokens` + */ + modifier hasAvailableFunds(uint256 _amount) { + require(withdrawableTokens[msg.sender] >= _amount, "can't withdraw more than balance"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFRequestIDBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFRequestIDBase.sol new file mode 100644 index 0000000..2668ead --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/VRFRequestIDBase.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +contract VRFRequestIDBase { + + /** + * @notice returns the seed which is actually input to the VRF coordinator + * + * @dev To prevent repetition of VRF output due to repetition of the + * @dev user-supplied seed, that seed is combined in a hash with the + * @dev user-specific nonce, and the address of the consuming contract. The + * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in + * @dev the final seed, but the nonce does protect against repetition in + * @dev requests which are included in a single block. + * + * @param _userSeed VRF seed input provided by user + * @param _requester Address of the requesting contract + * @param _nonce User-specific nonce at the time of the request + */ + function makeVRFInputSeed(bytes32 _keyHash, uint256 _userSeed, + address _requester, uint256 _nonce) + internal pure returns (uint256) + { + return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); + } + + /** + * @notice Returns the id for this request + * @param _keyHash The serviceAgreement ID to be used for this request + * @param _vRFInputSeed The seed to be passed directly to the VRF + * @return The id for this request + * + * @dev Note that _vRFInputSeed is not the seed passed by the consuming + * @dev contract, but the one generated by makeVRFInputSeed + */ + function makeRequestId( + bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/dev/BlockhashStore.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/dev/BlockhashStore.sol new file mode 100644 index 0000000..1c7419f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/dev/BlockhashStore.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +/** + * @title BlockhashStore + * @notice This contract provides a way to access blockhashes older than + * the 256 block limit imposed by the BLOCKHASH opcode. + * You may assume that any blockhash stored by the contract is correct. + * Note that the contract depends on the format of serialized Ethereum + * blocks. If a future hardfork of Ethereum changes that format, the + * logic in this contract may become incorrect and an updated version + * would have to be deployed. + */ +contract BlockhashStore { + + mapping(uint => bytes32) internal s_blockhashes; + + /** + * @notice stores blockhash of a given block, assuming it is available through BLOCKHASH + * @param n the number of the block whose blockhash should be stored + */ + function store(uint256 n) public { + bytes32 h = blockhash(n); + require(h != 0x0, "blockhash(n) failed"); + s_blockhashes[n] = h; + } + + + /** + * @notice stores blockhash of the earliest block still available through BLOCKHASH. + */ + function storeEarliest() external { + store(block.number - 256); + } + + /** + * @notice stores blockhash after verifying blockheader of child/subsequent block + * @param n the number of the block whose blockhash should be stored + * @param header the rlp-encoded blockheader of block n+1. We verify its correctness by checking + * that it hashes to a stored blockhash, and then extract parentHash to get the n-th blockhash. + */ + function storeVerifyHeader(uint256 n, bytes memory header) public { + require(keccak256(header) == s_blockhashes[n + 1], "header has unknown blockhash"); + + // At this point, we know that header is the correct blockheader for block n+1. + + // The header is an rlp-encoded list. The head item of that list is the 32-byte blockhash of the parent block. + // Based on how rlp works, we know that blockheaders always have the following form: + // 0xf9____a0PARENTHASH... + // ^ ^ ^ + // | | | + // | | +--- PARENTHASH is 32 bytes. rlpenc(PARENTHASH) is 0xa || PARENTHASH. + // | | + // | +--- 2 bytes containing the sum of the lengths of the encoded list items + // | + // +--- 0xf9 because we have a list and (sum of lengths of encoded list items) fits exactly into two bytes. + // + // As a consequence, the PARENTHASH is always at offset 4 of the rlp-encoded block header. + + bytes32 parentHash; + assembly { + parentHash := mload(add(header, 36)) // 36 = 32 byte offset for length prefix of ABI-encoded array + // + 4 byte offset of PARENTHASH (see above) + } + + s_blockhashes[n] = parentHash; + } + + /** + * @notice gets a blockhash from the store. If no hash is known, this function reverts. + * @param n the number of the block whose blockhash should be returned + */ + function getBlockhash(uint256 n) external view returns (bytes32) { + bytes32 h = s_blockhashes[n]; + require(h != 0x0, "blockhash not found in store"); + return h; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/dev/Denominations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/dev/Denominations.sol new file mode 100644 index 0000000..560a616 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/dev/Denominations.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +library Denominations { + address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 + address public constant USD = address(840); + address public constant GBP = address(826); + address public constant EUR = address(978); + address public constant JPY = address(392); + address public constant KRW = address(410); + address public constant CNY = address(156); + address public constant AUD = address(36); + address public constant CAD = address(124); + address public constant CHF = address(756); + address public constant ARS = address(32); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/examples/VRFD20.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/examples/VRFD20.sol new file mode 100644 index 0000000..0e4c041 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/examples/VRFD20.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../VRFConsumerBase.sol"; +import "../Owned.sol"; + +/** + * @notice A Chainlink VRF consumer which uses randomness to mimic the rolling + * of a 20 sided die + * @dev This is only an example implementation and not necessarily suitable for mainnet. + */ +contract VRFD20 is VRFConsumerBase, Owned { + using SafeMathChainlink for uint256; + + uint256 private constant ROLL_IN_PROGRESS = 42; + + bytes32 private s_keyHash; + uint256 private s_fee; + mapping(bytes32 => address) private s_rollers; + mapping(address => uint256) private s_results; + + event DiceRolled(bytes32 indexed requestId, address indexed roller); + event DiceLanded(bytes32 indexed requestId, uint256 indexed result); + + /** + * @notice Constructor inherits VRFConsumerBase + * + * @dev NETWORK: KOVAN + * @dev Chainlink VRF Coordinator address: 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9 + * @dev LINK token address: 0xa36085F69e2889c224210F603D836748e7dC0088 + * @dev Key Hash: 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4 + * @dev Fee: 0.1 LINK (100000000000000000) + * + * @param vrfCoordinator address of the VRF Coordinator + * @param link address of the LINK token + * @param keyHash bytes32 representing the hash of the VRF job + * @param fee uint256 fee to pay the VRF oracle + */ + constructor(address vrfCoordinator, address link, bytes32 keyHash, uint256 fee) + public + VRFConsumerBase(vrfCoordinator, link) + { + s_keyHash = keyHash; + s_fee = fee; + } + + /** + * @notice Requests randomness from a user-provided seed + * @dev Warning: if the VRF response is delayed, avoid calling requestRandomness repeatedly + * as that would give miners/VRF operators latitude about which VRF response arrives first. + * @dev You must review your implementation details with extreme care. + * + * @param roller address of the roller + */ + function rollDice(address roller) public onlyOwner returns (bytes32 requestId) { + require(LINK.balanceOf(address(this)) >= s_fee, "Not enough LINK to pay fee"); + require(s_results[roller] == 0, "Already rolled"); + requestId = requestRandomness(s_keyHash, s_fee); + s_rollers[requestId] = roller; + s_results[roller] = ROLL_IN_PROGRESS; + emit DiceRolled(requestId, roller); + } + + /** + * @notice Callback function used by VRF Coordinator to return the random number + * to this contract. + * @dev Some action on the contract state should be taken here, like storing the result. + * @dev WARNING: take care to avoid having multiple VRF requests in flight if their order of arrival would result + * in contract states with different outcomes. Otherwise miners or the VRF operator would could take advantage + * by controlling the order. + * @dev The VRF Coordinator will only send this function verified responses, and the parent VRFConsumerBase + * contract ensures that this method only receives randomness from the designated VRFCoordinator. + * + * @param requestId bytes32 + * @param randomness The random result returned by the oracle + */ + function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { + uint256 d20Value = randomness.mod(20).add(1); + s_results[s_rollers[requestId]] = d20Value; + emit DiceLanded(requestId, d20Value); + } + + /** + * @notice Get the house assigned to the player once the address has rolled + * @param player address + * @return house as a string + */ + function house(address player) public view returns (string memory) { + require(s_results[player] != 0, "Dice not rolled"); + require(s_results[player] != ROLL_IN_PROGRESS, "Roll in progress"); + return getHouseName(s_results[player]); + } + + /** + * @notice Withdraw LINK from this contract. + * @dev this is an example only, and in a real contract withdrawals should + * happen according to the established withdrawal pattern: + * https://docs.soliditylang.org/en/v0.4.24/common-patterns.html#withdrawal-from-contracts + * @param to the address to withdraw LINK to + * @param value the amount of LINK to withdraw + */ + function withdrawLINK(address to, uint256 value) public onlyOwner { + require(LINK.transfer(to, value), "Not enough LINK"); + } + + /** + * @notice Set the key hash for the oracle + * + * @param keyHash bytes32 + */ + function setKeyHash(bytes32 keyHash) public onlyOwner { + s_keyHash = keyHash; + } + + /** + * @notice Get the current key hash + * + * @return bytes32 + */ + function keyHash() public view returns (bytes32) { + return s_keyHash; + } + + /** + * @notice Set the oracle fee for requesting randomness + * + * @param fee uint256 + */ + function setFee(uint256 fee) public onlyOwner { + s_fee = fee; + } + + /** + * @notice Get the current fee + * + * @return uint256 + */ + function fee() public view returns (uint256) { + return s_fee; + } + + /** + * @notice Get the house name from the id + * @param id uint256 + * @return house name string + */ + function getHouseName(uint256 id) private pure returns (string memory) { + string[20] memory houseNames = [ + "Targaryen", + "Lannister", + "Stark", + "Tyrell", + "Baratheon", + "Martell", + "Tully", + "Bolton", + "Greyjoy", + "Arryn", + "Frey", + "Mormont", + "Tarley", + "Dayne", + "Umber", + "Valeryon", + "Manderly", + "Clegane", + "Glover", + "Karstark" + ]; + return houseNames[id.sub(1)]; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AccessControllerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AccessControllerInterface.sol new file mode 100644 index 0000000..4bf48bb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AccessControllerInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.6.0 <0.8.0; + +interface AccessControllerInterface { + function hasAccess(address user, bytes calldata data) external view returns (bool); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorInterface.sol new file mode 100644 index 0000000..4f48160 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorInterface.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface AggregatorInterface { + function latestAnswer() + external + view + returns ( + int256 + ); + + function latestTimestamp() + external + view + returns ( + uint256 + ); + + function latestRound() + external + view + returns ( + uint256 + ); + + function getAnswer( + uint256 roundId + ) + external + view + returns ( + int256 + ); + + function getTimestamp( + uint256 roundId + ) + external + view + returns ( + uint256 + ); + + event AnswerUpdated( + int256 indexed current, + uint256 indexed roundId, + uint256 updatedAt + ); + + event NewRound( + uint256 indexed roundId, + address indexed startedBy, + uint256 startedAt + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorV2V3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorV2V3Interface.sol new file mode 100644 index 0000000..6b4975e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorV2V3Interface.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./AggregatorInterface.sol"; +import "./AggregatorV3Interface.sol"; + +interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface +{ +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol new file mode 100644 index 0000000..a1af992 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface AggregatorV3Interface { + + function decimals() + external + view + returns ( + uint8 + ); + + function description() + external + view + returns ( + string memory + ); + + function version() + external + view + returns ( + uint256 + ); + + function getRoundData( + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorValidatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorValidatorInterface.sol new file mode 100644 index 0000000..50c3226 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/AggregatorValidatorInterface.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface AggregatorValidatorInterface { + function validate( + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ) external returns (bool); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol new file mode 100644 index 0000000..18927e6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +interface BlockHashStoreInterface { + function getBlockhash(uint256 number) external view returns (bytes32); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/ChainlinkRequestInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/ChainlinkRequestInterface.sol new file mode 100644 index 0000000..bcbd251 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/ChainlinkRequestInterface.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface ChainlinkRequestInterface { + function oracleRequest( + address sender, + uint256 requestPrice, + bytes32 serviceAgreementID, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external; + + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunctionId, + uint256 expiration + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/ENSInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/ENSInterface.sol new file mode 100644 index 0000000..158242c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/ENSInterface.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface ENSInterface { + + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + + function setSubnodeOwner(bytes32 node, bytes32 label, address _owner) external; + function setResolver(bytes32 node, address _resolver) external; + function setOwner(bytes32 node, address _owner) external; + function setTTL(bytes32 node, uint64 _ttl) external; + function owner(bytes32 node) external view returns (address); + function resolver(bytes32 node) external view returns (address); + function ttl(bytes32 node) external view returns (uint64); + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/FeedRegistryInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/FeedRegistryInterface.sol new file mode 100644 index 0000000..0eb2d1d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/FeedRegistryInterface.sol @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +import "./AggregatorV2V3Interface.sol"; + +interface FeedRegistryInterface { + struct Phase { + uint16 phaseId; + uint80 startingAggregatorRoundId; + uint80 endingAggregatorRoundId; + } + + event FeedProposed( + address indexed asset, + address indexed denomination, + address indexed proposedAggregator, + address currentAggregator, + address sender + ); + event FeedConfirmed( + address indexed asset, + address indexed denomination, + address indexed latestAggregator, + address previousAggregator, + uint16 nextPhaseId, + address sender + ); + + // V3 AggregatorV3Interface + + function decimals( + address base, + address quote + ) + external + view + returns ( + uint8 + ); + + function description( + address base, + address quote + ) + external + view + returns ( + string memory + ); + + function version( + address base, + address quote + ) + external + view + returns ( + uint256 + ); + + function latestRoundData( + address base, + address quote + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function getRoundData( + address base, + address quote, + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + // V2 AggregatorInterface + + function latestAnswer( + address base, + address quote + ) + external + view + returns ( + int256 answer + ); + + function latestTimestamp( + address base, + address quote + ) + external + view + returns ( + uint256 timestamp + ); + + function latestRound( + address base, + address quote + ) + external + view + returns ( + uint256 roundId + ); + + function getAnswer( + address base, + address quote, + uint256 roundId + ) + external + view + returns ( + int256 answer + ); + + function getTimestamp( + address base, + address quote, + uint256 roundId + ) + external + view + returns ( + uint256 timestamp + ); + + // Registry getters + + function getFeed( + address base, + address quote + ) + external + view + returns ( + AggregatorV2V3Interface aggregator + ); + + function getPhaseFeed( + address base, + address quote, + uint16 phaseId + ) + external + view + returns ( + AggregatorV2V3Interface aggregator + ); + + function isFeedEnabled( + address aggregator + ) + external + view + returns ( + bool + ); + + function getPhase( + address base, + address quote, + uint16 phaseId + ) + external + view + returns ( + Phase memory phase + ); + + // Round helpers + + function getRoundFeed( + address base, + address quote, + uint80 roundId + ) + external + view + returns ( + AggregatorV2V3Interface aggregator + ); + + function getPhaseRange( + address base, + address quote, + uint16 phaseId + ) + external + view + returns ( + uint80 startingRoundId, + uint80 endingRoundId + ); + + function getPreviousRoundId( + address base, + address quote, + uint80 roundId + ) external + view + returns ( + uint80 previousRoundId + ); + + function getNextRoundId( + address base, + address quote, + uint80 roundId + ) external + view + returns ( + uint80 nextRoundId + ); + + // Feed management + + function proposeFeed( + address base, + address quote, + address aggregator + ) external; + + function confirmFeed( + address base, + address quote, + address aggregator + ) external; + + // Proposed aggregator + + function getProposedFeed( + address base, + address quote + ) + external + view + returns ( + AggregatorV2V3Interface proposedAggregator + ); + + function proposedGetRoundData( + address base, + address quote, + uint80 roundId + ) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function proposedLatestRoundData( + address base, + address quote + ) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + // Phases + function getCurrentPhaseId( + address base, + address quote + ) + external + view + returns ( + uint16 currentPhaseId + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/FlagsInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/FlagsInterface.sol new file mode 100644 index 0000000..ad39cae --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/FlagsInterface.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface FlagsInterface { + function getFlag(address) external view returns (bool); + function getFlags(address[] calldata) external view returns (bool[] memory); + function raiseFlag(address) external; + function raiseFlags(address[] calldata) external; + function lowerFlags(address[] calldata) external; + function setRaisingAccessController(address) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol new file mode 100644 index 0000000..f092fc9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/KeeperCompatibleInterface.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface KeeperCompatibleInterface { + + /** + * @notice method that is simulated by the keepers to see if any work actually + * needs to be performed. This method does does not actually need to be + * executable, and since it is only ever simulated it can consume lots of gas. + * @dev To ensure that it is never called, you may want to add the + * cannotExecute modifier from KeeperBase to your implementation of this + * method. + * @param checkData specified in the upkeep registration so it is always the + * same for a registered upkeep. This can easily be broken down into specific + * arguments using `abi.decode`, so multiple upkeeps can be registered on the + * same contract and easily differentiated by the contract. + * @return upkeepNeeded boolean to indicate whether the keeper should call + * performUpkeep or not. + * @return performData bytes that the keeper should call performUpkeep with, if + * upkeep is needed. If you would like to encode data to decode later, try + * `abi.encode`. + */ + function checkUpkeep( + bytes calldata checkData + ) + external + returns ( + bool upkeepNeeded, + bytes memory performData + ); + + /** + * @notice method that is actually executed by the keepers, via the registry. + * The data returned by the checkUpkeep simulation will be passed into + * this method to actually be executed. + * @dev The input to this method should not be trusted, and the caller of the + * method should not even be restricted to any single registry. Anyone should + * be able call it, and the input should be validated, there is no guarantee + * that the data passed in is the performData returned from checkUpkeep. This + * could happen due to malicious keepers, racing keepers, or simply a state + * change while the performUpkeep transaction is waiting for confirmation. + * Always validate the data passed in. + * @param performData is the data which was passed back from the checkData + * simulation. If it is encoded, it can easily be decoded into other types by + * calling `abi.decode`. This data should not be trusted, and should be + * validated against the contract's current state. + */ + function performUpkeep( + bytes calldata performData + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/LinkTokenInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/LinkTokenInterface.sol new file mode 100644 index 0000000..eeb9444 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/LinkTokenInterface.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +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); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/OracleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/OracleInterface.sol new file mode 100644 index 0000000..96b49f0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/OracleInterface.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface OracleInterface { + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) external returns (bool); + function getAuthorizationStatus(address node) external view returns (bool); + function setFulfillmentPermission(address node, bool allowed) external; + function withdraw(address recipient, uint256 amount) external; + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/PointerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/PointerInterface.sol new file mode 100644 index 0000000..e1cac19 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/PointerInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface PointerInterface { + function getAddress() external view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/WithdrawalInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/WithdrawalInterface.sol new file mode 100644 index 0000000..e83d327 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/interfaces/WithdrawalInterface.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface WithdrawalInterface { + /** + * @notice transfer LINK held by the contract belonging to msg.sender to + * another address + * @param recipient is the address to send the LINK to + * @param amount is the amount of LINK to send + */ + function withdraw(address recipient, uint256 amount) external; + + /** + * @notice query the available amount of LINK to withdraw by msg.sender + */ + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/AccessControlTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/AccessControlTestHelper.sol new file mode 100644 index 0000000..e6ff4e5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/AccessControlTestHelper.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../SimpleReadAccessController.sol"; + +contract AccessControlTestHelper is SimpleReadAccessController { + + int256 private value; + + constructor(int256 _value) + public + { + value = _value; + } + + function getValue() + external + view + checkAccess() + returns (int256) + { + return value; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/AggregatorValidatorMock.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/AggregatorValidatorMock.sol new file mode 100644 index 0000000..7af4717 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/AggregatorValidatorMock.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../interfaces/AggregatorValidatorInterface.sol"; + +contract AggregatorValidatorMock is AggregatorValidatorInterface { + uint256 public previousRoundId; + int256 public previousAnswer; + uint256 public currentRoundId; + int256 public currentAnswer; + + event Validated( + uint256 _previousRoundId, + int256 indexed _previousAnswer, + uint256 _currentRoundId, + int256 indexed _currentAnswer + ); + + function validate( + uint256 _previousRoundId, + int256 _previousAnswer, + uint256 _currentRoundId, + int256 _currentAnswer + ) + external + override + returns (bool) + { + emit Validated( + _previousRoundId, + _previousAnswer, + _currentRoundId, + _currentAnswer + ); + return true; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/BasicConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/BasicConsumer.sol new file mode 100644 index 0000000..f657fb0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/BasicConsumer.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./Consumer.sol"; + +contract BasicConsumer is Consumer { + + constructor(address _link, address _oracle, bytes32 _specId) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/BlockhashStoreTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/BlockhashStoreTestHelper.sol new file mode 100644 index 0000000..4278c3a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/BlockhashStoreTestHelper.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../dev/BlockhashStore.sol"; + +contract BlockhashStoreTestHelper is BlockhashStore { + function godmodeSetHash(uint256 n, bytes32 h) public { + s_blockhashes[n] = h; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ChainlinkClientTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ChainlinkClientTestHelper.sol new file mode 100644 index 0000000..2a390dc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ChainlinkClientTestHelper.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../ChainlinkClient.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract ChainlinkClientTestHelper is ChainlinkClient { + using SafeMathChainlink for uint256; + + constructor( + address _link, + address _oracle + ) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + } + + event Request( + bytes32 id, + address callbackAddress, + bytes4 callbackfunctionSelector, + bytes data + ); + event LinkAmount( + uint256 amount + ); + + function publicNewRequest( + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature + ) + public + { + Chainlink.Request memory req = buildChainlinkRequest( + _id, _address, bytes4(keccak256(_fulfillmentSignature))); + emit Request( + req.id, + req.callbackAddress, + req.callbackFunctionId, + req.buf.buf + ); + } + + function publicRequest( + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) + public + { + Chainlink.Request memory req = buildChainlinkRequest( + _id, _address, bytes4(keccak256(_fulfillmentSignature))); + sendChainlinkRequest(req, _wei); + } + + function publicRequestRunTo( + address _oracle, + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) + public + { + Chainlink.Request memory run = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + sendChainlinkRequestTo(_oracle, run, _wei); + } + + function publicCancelRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function publicChainlinkToken() public view returns (address) { + return chainlinkTokenAddress(); + } + + function publicFulfillChainlinkRequest(bytes32 _requestId, bytes32) public { + fulfillRequest(_requestId, bytes32(0)); + } + + function fulfillRequest(bytes32 _requestId, bytes32) + public + { + validateChainlinkCallback(_requestId); + } + + function publicLINK( + uint256 _amount + ) + public + { + emit LinkAmount(LINK.mul(_amount)); + } + + function publicOracleAddress() + public + view + returns ( + address + ) + { + return chainlinkOracleAddress(); + } + + function publicAddExternalRequest( + address _oracle, + bytes32 _requestId + ) + public + { + addChainlinkExternalRequest(_oracle, _requestId); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ChainlinkTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ChainlinkTestHelper.sol new file mode 100644 index 0000000..09b46a9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ChainlinkTestHelper.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../Chainlink.sol"; +import "../vendor/CBORChainlink.sol"; +import "../vendor/BufferChainlink.sol"; + +contract ChainlinkTestHelper { + using Chainlink for Chainlink.Request; + using CBORChainlink for BufferChainlink.buffer; + + Chainlink.Request private req; + + event RequestData(bytes payload); + + function closeEvent() public { + emit RequestData(req.buf.buf); + } + + function setBuffer(bytes memory data) public { + Chainlink.Request memory r2 = req; + r2.setBuffer(data); + req = r2; + } + + function add(string memory _key, string memory _value) public { + Chainlink.Request memory r2 = req; + r2.add(_key, _value); + req = r2; + } + + function addBytes(string memory _key, bytes memory _value) public { + Chainlink.Request memory r2 = req; + r2.addBytes(_key, _value); + req = r2; + } + + function addInt(string memory _key, int256 _value) public { + Chainlink.Request memory r2 = req; + r2.addInt(_key, _value); + req = r2; + } + + function addUint(string memory _key, uint256 _value) public { + Chainlink.Request memory r2 = req; + r2.addUint(_key, _value); + req = r2; + } + + // Temporarily have method receive bytes32[] memory until experimental + // string[] memory can be invoked from truffle tests. + function addStringArray(string memory _key, bytes32[] memory _values) public { + string[] memory strings = new string[](_values.length); + for (uint256 i = 0; i < _values.length; i++) { + strings[i] = bytes32ToString(_values[i]); + } + Chainlink.Request memory r2 = req; + r2.addStringArray(_key, strings); + req = r2; + } + + function bytes32ToString(bytes32 x) private pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint charCount = 0; + for (uint j = 0; j < 32; j++) { + byte char = byte(bytes32(uint(x) * 2 ** (8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/CheckedMathTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/CheckedMathTestHelper.sol new file mode 100644 index 0000000..1306c53 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/CheckedMathTestHelper.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../CheckedMath.sol"; + +contract CheckedMathTestHelper { + using CheckedMath for int256; + + function add(int256 a, int256 b) + external + pure + returns (int256 result, bool ok) + { + return a.add(b); + } + + function sub(int256 a, int256 b) + external + pure + returns (int256 result, bool ok) + { + return a.sub(b); + } + + function mul(int256 a, int256 b) + external + pure + returns (int256 result, bool ok) + { + return a.mul(b); + } + + function div(int256 a, int256 b) + external + pure + returns (int256 result, bool ok) + { + return a.div(b); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ConcreteSignedSafeMath.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ConcreteSignedSafeMath.sol new file mode 100644 index 0000000..14be546 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/ConcreteSignedSafeMath.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../SignedSafeMath.sol"; + +contract ConcreteSignedSafeMath { + function testAdd(int256 _a, int256 _b) + external + pure + returns (int256) + { + return SignedSafeMath.add(_a, _b); + } + + function testAvg(int256 _a, int256 _b) + external + pure + returns (int256) + { + return SignedSafeMath.avg(_a, _b); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/Consumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/Consumer.sol new file mode 100644 index 0000000..1e49bce --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/Consumer.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../ChainlinkClient.sol"; + +contract Consumer is ChainlinkClient { + bytes32 internal specId; + bytes32 public currentPrice; + + event RequestFulfilled( + bytes32 indexed requestId, // User-defined ID + bytes32 indexed price + ); + + function requestEthereumPrice(string memory _currency, uint256 _payment) public { + requestEthereumPriceByCallback(_currency, _payment, address(this)); + } + + function requestEthereumPriceByCallback(string memory _currency, uint256 _payment, address _callback) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, _callback, this.fulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = _currency; + req.addStringArray("path", path); + sendChainlinkRequest(req, _payment); + } + + function cancelRequest( + address _oracle, + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(_oracle); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function withdrawLink() public { + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer"); + } + + function addExternalRequest(address _oracle, bytes32 _requestId) external { + addChainlinkExternalRequest(_oracle, _requestId); + } + + function fulfill(bytes32 _requestId, bytes32 _price) + public + recordChainlinkFulfillment(_requestId) + { + emit RequestFulfilled(_requestId, _price); + currentPrice = _price; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/EmptyOracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/EmptyOracle.sol new file mode 100644 index 0000000..f076d12 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/EmptyOracle.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../interfaces/ChainlinkRequestInterface.sol"; +import "../interfaces/OracleInterface.sol"; + +/* solhint-disable no-empty-blocks */ + +contract EmptyOracle is ChainlinkRequestInterface, OracleInterface { + + function cancelOracleRequest(bytes32, uint256, bytes4, uint256) external override {} + function fulfillOracleRequest(bytes32, uint256, address, bytes4, uint256, bytes32) external override returns (bool) {} + function getAuthorizationStatus(address) external override view returns (bool) { return false; } + function onTokenTransfer(address, uint256, bytes calldata) external pure {} + function oracleRequest(address, uint256, bytes32, address, bytes4, uint256, uint256, bytes calldata) external override {} + function setFulfillmentPermission(address, bool) external override {} + function withdraw(address, uint256) external override {} + function withdrawable() external override view returns (uint256) {} + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/FlagsTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/FlagsTestHelper.sol new file mode 100644 index 0000000..0ed37b6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/FlagsTestHelper.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../Flags.sol"; + +contract FlagsTestHelper { + Flags public flags; + + constructor( + address flagsContract + ) + public + { + flags = Flags(flagsContract); + } + + function getFlag( + address subject + ) + external + view + returns(bool) + { + return flags.getFlag(subject); + } + + function getFlags( + address[] calldata subjects + ) + external + view + returns(bool[] memory) + { + return flags.getFlags(subjects); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/FluxAggregatorTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/FluxAggregatorTestHelper.sol new file mode 100644 index 0000000..24af37f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/FluxAggregatorTestHelper.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../FluxAggregator.sol"; + +contract FluxAggregatorTestHelper { + + uint80 public requestedRoundId; + + function readOracleRoundState(address _aggregator, address _oracle) + external + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + FluxAggregator(_aggregator).oracleRoundState(_oracle, 0); + } + + function readGetRoundData(address _aggregator, uint80 _roundID) + external + { + FluxAggregator(_aggregator).getRoundData(_roundID); + } + + function readLatestRoundData(address _aggregator) + external + { + FluxAggregator(_aggregator).latestRoundData(); + } + + function readLatestAnswer(address _aggregator) + external + { + FluxAggregator(_aggregator).latestAnswer(); + } + + function readLatestTimestamp(address _aggregator) + external + { + FluxAggregator(_aggregator).latestTimestamp(); + } + + function readLatestRound(address _aggregator) + external + { + FluxAggregator(_aggregator).latestRound(); + } + + function requestNewRound(address _aggregator) + external + { + requestedRoundId = FluxAggregator(_aggregator).requestNewRound(); + } + + function readGetAnswer(address _aggregator, uint256 _roundID) + external + { + FluxAggregator(_aggregator).getAnswer(_roundID); + } + + function readGetTimestamp(address _aggregator, uint256 _roundID) + external + { + FluxAggregator(_aggregator).getTimestamp(_roundID); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/GasGuzzler.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/GasGuzzler.sol new file mode 100644 index 0000000..5b30f1b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/GasGuzzler.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +contract GasGuzzler { + fallback() external payable { + while (true) { + } + } +} + diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/GasGuzzlingConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/GasGuzzlingConsumer.sol new file mode 100644 index 0000000..8122f45 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/GasGuzzlingConsumer.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "./Consumer.sol"; + +contract GasGuzzlingConsumer is Consumer{ + + constructor(address _link, address _oracle, bytes32 _specId) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + + function gassyRequestEthereumPrice(uint256 _payment) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, address(this), this.gassyFulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = "USD"; + req.addStringArray("path", path); + sendChainlinkRequest(req, _payment); + } + + function gassyFulfill(bytes32 _requestId, bytes32 _price) + public + recordChainlinkFulfillment(_requestId) + { + while(true){ + } + } + + function gassyMultiWordRequest(uint256 _payment) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, address(this), this.gassyMultiWordFulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = "USD"; + req.addStringArray("path", path); + sendChainlinkRequest(req, _payment); + } + + function gassyMultiWordFulfill(bytes32 _requestId, bytes memory _price) + public + recordChainlinkFulfillment(_requestId) + { + while(true){ + } + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/KeeperCompatibleTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/KeeperCompatibleTestHelper.sol new file mode 100644 index 0000000..68a4d1f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/KeeperCompatibleTestHelper.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../KeeperCompatible.sol"; + +contract KeeperCompatibleTestHelper is KeeperCompatible { + function checkUpkeep(bytes calldata) external override returns (bool, bytes memory) {} + + function performUpkeep(bytes calldata) external override {} + + function testCannotExecute() public view cannotExecute {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MaliciousMultiWordConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MaliciousMultiWordConsumer.sol new file mode 100644 index 0000000..cd36836 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MaliciousMultiWordConsumer.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../ChainlinkClient.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract MaliciousMultiWordConsumer is ChainlinkClient { + using SafeMathChainlink for uint256; + + uint256 constant private ORACLE_PAYMENT = 1 * LINK; + uint256 private expiration; + + constructor(address _link, address _oracle) public payable { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + } + + receive() external payable {} // solhint-disable-line no-empty-blocks + + function requestData(bytes32 _id, bytes memory _callbackFunc) public { + Chainlink.Request memory req = buildChainlinkRequest(_id, address(this), bytes4(keccak256(_callbackFunc))); + expiration = now.add(5 minutes); // solhint-disable-line not-rely-on-time + sendChainlinkRequest(req, ORACLE_PAYMENT); + } + + function assertFail(bytes32, bytes memory) public pure { + assert(1 == 2); + } + + function cancelRequestOnFulfill(bytes32 _requestId, bytes memory) public { + cancelChainlinkRequest( + _requestId, + ORACLE_PAYMENT, + this.cancelRequestOnFulfill.selector, + expiration); + } + + function remove() public { + selfdestruct(address(0)); + } + + function stealEthCall(bytes32 _requestId, bytes memory) public recordChainlinkFulfillment(_requestId) { + (bool success,) = address(this).call.value(100)(""); // solhint-disable-line avoid-call-value + require(success, "Call failed"); + } + + function stealEthSend(bytes32 _requestId, bytes memory) public recordChainlinkFulfillment(_requestId) { + // solhint-disable-next-line check-send-result + bool success = address(this).send(100); // solhint-disable-line multiple-sends + require(success, "Send failed"); + } + + function stealEthTransfer(bytes32 _requestId, bytes memory) public recordChainlinkFulfillment(_requestId) { + address(this).transfer(100); + } + + function doesNothing(bytes32, bytes memory) public pure {} // solhint-disable-line no-empty-blocks +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MedianTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MedianTestHelper.sol new file mode 100644 index 0000000..5386790 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MedianTestHelper.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../Median.sol"; + +contract MedianTestHelper { + function publicGet(int256[] memory list) + public + pure + returns (int256) + { + return Median.calculate(list); + } + + function publicQuickselectTwo(int256[] memory list, uint256 k1, uint256 k2) + public + pure + returns (int256, int256) + { + return Median.quickselectTwo(list, 0, list.length - 1, k1, k2); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockOracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockOracle.sol new file mode 100644 index 0000000..7c86d29 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockOracle.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../LinkTokenReceiver.sol"; +import "../interfaces/ChainlinkRequestInterface.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../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/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockV2Aggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockV2Aggregator.sol new file mode 100644 index 0000000..aab4624 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockV2Aggregator.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../interfaces/AggregatorInterface.sol"; + +/** + * @title MockV2Aggregator + * @notice Based on the HistoricAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV2Aggregator is AggregatorInterface { + int256 public override latestAnswer; + uint256 public override latestTimestamp; + uint256 public override latestRound; + + mapping(uint256 => int256) public override getAnswer; + mapping(uint256 => uint256) public override getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor( + int256 _initialAnswer + ) public { + updateAnswer(_initialAnswer); + } + + function updateAnswer( + int256 _answer + ) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + } + + function updateRoundData( + uint256 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockV3Aggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockV3Aggregator.sol new file mode 100644 index 0000000..b382281 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MockV3Aggregator.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../interfaces/AggregatorV2V3Interface.sol"; + +/** + * @title MockV3Aggregator + * @notice Based on the FluxAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV3Aggregator is AggregatorV2V3Interface { + uint256 constant public override version = 0; + + uint8 public override decimals; + int256 public override latestAnswer; + uint256 public override latestTimestamp; + uint256 public override latestRound; + + mapping(uint256 => int256) public override getAnswer; + mapping(uint256 => uint256) public override getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor( + uint8 _decimals, + int256 _initialAnswer + ) public { + decimals = _decimals; + updateAnswer(_initialAnswer); + } + + function updateAnswer( + int256 _answer + ) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + getStartedAt[latestRound] = block.timestamp; + } + + function updateRoundData( + uint80 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } + + function getRoundData(uint80 _roundId) + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + _roundId, + getAnswer[_roundId], + getStartedAt[_roundId], + getTimestamp[_roundId], + _roundId + ); + } + + function latestRoundData() + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + uint80(latestRound), + getAnswer[latestRound], + getStartedAt[latestRound], + getTimestamp[latestRound], + uint80(latestRound) + ); + } + + function description() + external + view + override + returns (string memory) + { + return "v0.6/tests/MockV3Aggregator.sol"; + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MultiWordConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MultiWordConsumer.sol new file mode 100644 index 0000000..a2ff5d4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/MultiWordConsumer.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../ChainlinkClient.sol"; + +contract MultiWordConsumer is ChainlinkClient{ + bytes32 internal specId; + bytes public currentPrice; + + bytes32 public first; + bytes32 public second; + + event RequestFulfilled( + bytes32 indexed requestId, // User-defined ID + bytes indexed price + ); + + event RequestMultipleFulfilled( + bytes32 indexed requestId, + bytes32 indexed first, + bytes32 indexed second + ); + + constructor(address _link, address _oracle, bytes32 _specId) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + + function requestEthereumPrice(string memory _currency, uint256 _payment) public { + requestEthereumPriceByCallback(_currency, _payment, address(this)); + } + + function requestEthereumPriceByCallback(string memory _currency, uint256 _payment, address _callback) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, _callback, this.fulfillBytes.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = _currency; + req.addStringArray("path", path); + sendChainlinkRequest(req, _payment); + } + + function requestMultipleParameters(string memory _currency, uint256 _payment) public { + Chainlink.Request memory req = buildChainlinkRequest(specId, address(this), this.fulfillMultipleParameters.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = _currency; + req.addStringArray("path", path); + sendChainlinkRequest(req, _payment); + } + + function cancelRequest( + address _oracle, + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(_oracle); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function withdrawLink() public { + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer"); + } + + function addExternalRequest(address _oracle, bytes32 _requestId) external { + addChainlinkExternalRequest(_oracle, _requestId); + } + + function fulfillMultipleParameters(bytes32 _requestId, bytes32 _first, bytes32 _second) + public + recordChainlinkFulfillment(_requestId) + { + emit RequestMultipleFulfilled(_requestId, _first, _second); + first = _first; + second = _second; + } + + function fulfillBytes(bytes32 _requestId, bytes memory _price) + public + recordChainlinkFulfillment(_requestId) + { + emit RequestFulfilled(_requestId, _price); + currentPrice = _price; + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/OwnedTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/OwnedTestHelper.sol new file mode 100644 index 0000000..c71c7bf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/OwnedTestHelper.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "../Owned.sol"; + +contract OwnedTestHelper is Owned { + + event Here(); + + function modifierOnlyOwner() + public + onlyOwner() + { + emit Here(); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/Reverter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/Reverter.sol new file mode 100644 index 0000000..a262fbe --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/Reverter.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +contract Reverter { + + fallback() external payable { + require(false, "Raised by Reverter.sol"); + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFConsumer.sol new file mode 100644 index 0000000..56c09c2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFConsumer.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../interfaces/LinkTokenInterface.sol"; +import "../VRFCoordinator.sol"; +import "../VRFConsumerBase.sol"; + +contract VRFConsumer is VRFConsumerBase { + + uint256 public randomnessOutput; + bytes32 public requestId; + + constructor(address _vrfCoordinator, address _link) public + // solhint-disable-next-line no-empty-blocks + VRFConsumerBase(_vrfCoordinator, _link) { /* empty */ } + + function fulfillRandomness(bytes32 _requestId, uint256 _randomness) + internal override + { + randomnessOutput = _randomness; + requestId = _requestId; + } + + function testRequestRandomness(bytes32 _keyHash, uint256 _fee) + external returns (bytes32 requestId) + { + return requestRandomness(_keyHash, _fee); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFCoordinatorMock.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFCoordinatorMock.sol new file mode 100644 index 0000000..dd8b806 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFCoordinatorMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../interfaces/LinkTokenInterface.sol"; +import "../VRFConsumerBase.sol"; + +contract VRFCoordinatorMock { + + LinkTokenInterface public LINK; + + event RandomnessRequest(address indexed sender, bytes32 indexed keyHash, uint256 indexed seed); + + constructor(address linkAddress) public { + LINK = LinkTokenInterface(linkAddress); + } + + function onTokenTransfer(address sender, uint256 fee, bytes memory _data) + public + onlyLINK + { + (bytes32 keyHash, uint256 seed) = abi.decode(_data, (bytes32, uint256)); + emit RandomnessRequest(sender, keyHash, seed); + } + + function callBackWithRandomness( + bytes32 requestId, + uint256 randomness, + address consumerContract + ) public { + VRFConsumerBase v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomness.selector, requestId, randomness); + uint256 b = 206000; + require(gasleft() >= b, "not enough gas for consumer"); + (bool success,) = consumerContract.call(resp); + } + + modifier onlyLINK() { + require(msg.sender == address(LINK), "Must use LINK token"); + _; + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFRequestIDBaseTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFRequestIDBaseTestHelper.sol new file mode 100644 index 0000000..f1b81f2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFRequestIDBaseTestHelper.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../VRFRequestIDBase.sol"; + +contract VRFRequestIDBaseTestHelper is VRFRequestIDBase { + + function makeVRFInputSeed_(bytes32 _keyHash, uint256 _userSeed, + address _requester, uint256 _nonce) + public pure returns (uint256) { + return makeVRFInputSeed(_keyHash, _userSeed, _requester, _nonce); + } + + function makeRequestId_( + bytes32 _keyHash, uint256 _vRFInputSeed) public pure returns (bytes32) { + return makeRequestId(_keyHash, _vRFInputSeed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFTestHelper.sol new file mode 100644 index 0000000..d79e71d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/tests/VRFTestHelper.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.6; + +import "../VRF.sol"; + +/** *********************************************************************** + @notice Testing harness for VRF.sol, exposing its internal methods. Not to + @notice be used for production. +*/ +contract VRFTestHelper is VRF { + function bigModExp_(uint256 base, uint256 exponent) public view returns (uint256) { + return super.bigModExp(base, exponent); + } + function squareRoot_(uint256 x) public view returns (uint256) { + return super.squareRoot(x); + } + function ySquared_(uint256 x) public pure returns (uint256) { + return super.ySquared(x); + } + function fieldHash_(bytes memory b) public pure returns (uint256) { + return super.fieldHash(b); + } + function hashToCurve_(uint256[2] memory pk, uint256 x) public view returns(uint256[2] memory) { + return super.hashToCurve(pk, x); + } + function ecmulVerify_(uint256[2] memory x, uint256 scalar, uint256[2] memory q) public pure returns (bool) { + return super.ecmulVerify(x, scalar, q); + } + function projectiveECAdd_(uint256 px, uint256 py, uint256 qx, uint256 qy) public pure returns(uint256, uint256, uint256) { + return super.projectiveECAdd(px, py, qx, qy); + } + function affineECAdd_(uint256[2] memory p1, uint256[2] memory p2, uint256 invZ) public pure returns (uint256[2] memory) { + return super.affineECAdd(p1, p2, invZ); + } + function verifyLinearCombinationWithGenerator_(uint256 c, uint256[2] memory p, uint256 s, address lcWitness) public pure returns (bool) { + return super.verifyLinearCombinationWithGenerator(c, p, s, lcWitness); + } + function linearCombination_(uint256 c, uint256[2] memory p1, uint256[2] memory cp1Witness, uint256 s, uint256[2] memory p2, uint256[2] memory sp2Witness, uint256 zInv) public pure returns (uint256[2] memory) { + return super.linearCombination(c, p1, cp1Witness, s, p2, sp2Witness, zInv); + } + function scalarFromCurvePoints_(uint256[2] memory hash, uint256[2] memory pk, uint256[2] memory gamma, address uWitness, uint256[2] memory v) public pure returns (uint256) { + return super.scalarFromCurvePoints(hash, pk, gamma, uWitness, v); + } + function verifyVRFProof_( + uint256[2] memory pk, uint256[2] memory gamma, uint256 c, uint256 s, + uint256 seed, address uWitness, uint256[2] memory cGammaWitness, + uint256[2] memory sHashWitness, uint256 zInv) + public view { + super.verifyVRFProof(pk, gamma, c, s, seed, uWitness, cGammaWitness, sHashWitness, zInv); + } + function randomValueFromVRFProof_(bytes memory proof) + public view returns (uint256 output) { + return super.randomValueFromVRFProof(proof); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/BufferChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/BufferChainlink.sol new file mode 100644 index 0000000..2ef5342 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/BufferChainlink.sol @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** +* @dev A library for working with mutable byte buffers in Solidity. +* +* Byte buffers are mutable and expandable, and provide a variety of primitives +* for writing to them. At any time you can fetch a bytes object containing the +* current contents of the buffer. The bytes object should not be stored between +* operations, as it may change due to resizing of the buffer. +*/ +library BufferChainlink { + /** + * @dev Represents a mutable buffer. Buffers have a current value (buf) and + * a capacity. The capacity may be longer than the current value, in + * which case it can be extended without the need to allocate more memory. + */ + struct buffer { + bytes buf; + uint capacity; + } + + /** + * @dev Initializes a buffer with an initial capacity. + * @param buf The buffer to initialize. + * @param capacity The number of bytes of space to allocate the buffer. + * @return The buffer, for chaining. + */ + function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + // Allocate space for the buffer data + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + mstore(0x40, add(32, add(ptr, capacity))) + } + return buf; + } + + /** + * @dev Initializes a new buffer from an existing bytes object. + * Changes to the buffer may mutate the original value. + * @param b The bytes object to initialize the buffer with. + * @return A new buffer. + */ + function fromBytes(bytes memory b) internal pure returns(buffer memory) { + buffer memory buf; + buf.buf = b; + buf.capacity = b.length; + return buf; + } + + function resize(buffer memory buf, uint capacity) private pure { + bytes memory oldbuf = buf.buf; + init(buf, capacity); + append(buf, oldbuf); + } + + function max(uint a, uint b) private pure returns(uint) { + if (a > b) { + return a; + } + return b; + } + + /** + * @dev Sets buffer length to 0. + * @param buf The buffer to truncate. + * @return The original buffer, for chaining.. + */ + function truncate(buffer memory buf) internal pure returns (buffer memory) { + assembly { + let bufptr := mload(buf) + mstore(bufptr, 0) + } + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) { + require(len <= data.length); + + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint dest; + uint src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(data, 32) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + + return buf; + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, len); + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, data.length); + } + + /** + * @dev Writes a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write the byte at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) { + if (off >= buf.capacity) { + resize(buf, buf.capacity * 2); + } + + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Address = buffer address + sizeof(buffer length) + off + let dest := add(add(bufptr, off), 32) + mstore8(dest, data) + // Update buffer length if we extended it + if eq(off, buflen) { + mstore(bufptr, add(buflen, 1)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) { + return writeUint8(buf, buf.buf.length, data); + } + + /** + * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (left-aligned). + * @return The original buffer, for chaining. + */ + function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint mask = 256 ** len - 1; + // Right-align data + data = data >> (8 * (32 - len)); + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + sizeof(buffer length) + off + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) { + return write(buf, off, bytes32(data), 20); + } + + /** + * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chhaining. + */ + function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, bytes32(data), 20); + } + + /** + * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, 32); + } + + /** + * @dev Writes an integer to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (right-aligned). + * @return The original buffer, for chaining. + */ + function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint mask = 256 ** len - 1; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + off + sizeof(buffer length) + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer. + */ + function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { + return writeInt(buf, buf.buf.length, data, len); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/CBORChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/CBORChainlink.sol new file mode 100644 index 0000000..90a1b1b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/CBORChainlink.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity >= 0.4.19; + +import { BufferChainlink } from "./BufferChainlink.sol"; + +library CBORChainlink { + using BufferChainlink for BufferChainlink.buffer; + + uint8 private constant MAJOR_TYPE_INT = 0; + uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 private constant MAJOR_TYPE_BYTES = 2; + uint8 private constant MAJOR_TYPE_STRING = 3; + uint8 private constant MAJOR_TYPE_ARRAY = 4; + uint8 private constant MAJOR_TYPE_MAP = 5; + uint8 private constant MAJOR_TYPE_TAG = 6; + uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; + + uint8 private constant TAG_TYPE_BIGNUM = 2; + uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; + + function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure { + if(value <= 23) { + buf.appendUint8(uint8((major << 5) | value)); + } else if(value <= 0xFF) { + buf.appendUint8(uint8((major << 5) | 24)); + buf.appendInt(value, 1); + } else if(value <= 0xFFFF) { + buf.appendUint8(uint8((major << 5) | 25)); + buf.appendInt(value, 2); + } else if(value <= 0xFFFFFFFF) { + buf.appendUint8(uint8((major << 5) | 26)); + buf.appendInt(value, 4); + } else { + buf.appendUint8(uint8((major << 5) | 27)); + buf.appendInt(value, 8); + } + } + + function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure { + buf.appendUint8(uint8((major << 5) | 31)); + } + + function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure { + if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, value); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } + } + + function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure { + if(value < -0x10000000000000000) { + encodeSignedBigNum(buf, value); + } else if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, uint(value)); + } else if(value >= 0) { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(-1 - value)); + } + } + + function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); + buf.append(value); + } + + function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); + encodeBytes(buf, abi.encode(value)); + } + + function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); + encodeBytes(buf, abi.encode(uint(-1 - input))); + } + + function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); + buf.append(bytes(value)); + } + + function startArray(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); + } + + function startMap(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); + } + + function endSequence(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/ENSResolver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/ENSResolver.sol new file mode 100644 index 0000000..a2aff79 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/ENSResolver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +abstract contract ENSResolver { + function addr(bytes32 node) public view virtual returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/Ownable.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/Ownable.sol new file mode 100644 index 0000000..f0299db --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/Ownable.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be aplied to your functions to restrict their use to + * the owner. + * + * This contract has been modified to remove the revokeOwnership function + */ +contract Ownable { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor () internal { + _owner = msg.sender; + emit OwnershipTransferred(address(0), _owner); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(isOwner(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Returns true if the caller is the current owner. + */ + function isOwner() public view returns (bool) { + return msg.sender == _owner; + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public onlyOwner { + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + */ + function _transferOwnership(address newOwner) internal { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + emit OwnershipTransferred(_owner, newOwner); + _owner = newOwner; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/SafeMathChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/SafeMathChainlink.sol new file mode 100644 index 0000000..39d73a5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/SafeMathChainlink.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMathChainlink { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SafeMath: subtraction overflow"); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/AuthorizedForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/AuthorizedForwarder.sol new file mode 100644 index 0000000..fa3faf4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/AuthorizedForwarder.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interfaces/OperatorInterface.sol"; +import "./ConfirmedOwnerWithProposal.sol"; +import "./AuthorizedReceiver.sol"; +import "./vendor/Address.sol"; + +contract AuthorizedForwarder is ConfirmedOwnerWithProposal, AuthorizedReceiver { + using Address for address; + + address public immutable getChainlinkToken; + + event OwnershipTransferRequestedWithMessage(address indexed from, address indexed to, bytes message); + + constructor( + address link, + address owner, + address recipient, + bytes memory message + ) ConfirmedOwnerWithProposal(owner, recipient) { + getChainlinkToken = link; + if (recipient != address(0)) { + emit OwnershipTransferRequestedWithMessage(owner, recipient, message); + } + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual returns (string memory) { + return "AuthorizedForwarder 1.0.0"; + } + + /** + * @notice Forward a call to another contract + * @dev Only callable by an authorized sender + * @param to address + * @param data to forward + */ + function forward(address to, bytes calldata data) external validateAuthorizedSender { + require(to != getChainlinkToken, "Cannot forward to Link token"); + _forward(to, data); + } + + /** + * @notice Forward a call to another contract + * @dev Only callable by the owner + * @param to address + * @param data to forward + */ + function ownerForward(address to, bytes calldata data) external onlyOwner { + _forward(to, data); + } + + /** + * @notice Transfer ownership with instructions for recipient + * @param to address proposed recipient of ownership + * @param message instructions for recipient upon accepting ownership + */ + function transferOwnershipWithMessage(address to, bytes calldata message) external { + transferOwnership(to); + emit OwnershipTransferRequestedWithMessage(msg.sender, to, message); + } + + /** + * @notice concrete implementation of AuthorizedReceiver + * @return bool of whether sender is authorized + */ + function _canSetAuthorizedSenders() internal view override returns (bool) { + return owner() == msg.sender; + } + + /** + * @notice common forwarding functionality and validation + */ + function _forward(address to, bytes calldata data) private { + require(to.isContract(), "Must forward to a contract"); + (bool status, ) = to.call(data); + require(status, "Forwarded call failed"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/AuthorizedReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/AuthorizedReceiver.sol new file mode 100644 index 0000000..4041d89 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/AuthorizedReceiver.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interfaces/AuthorizedReceiverInterface.sol"; + +abstract contract AuthorizedReceiver is AuthorizedReceiverInterface { + mapping(address => bool) private s_authorizedSenders; + address[] private s_authorizedSenderList; + + event AuthorizedSendersChanged(address[] senders, address changedBy); + + /** + * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. + * @param senders The addresses of the authorized Chainlink node + */ + function setAuthorizedSenders(address[] calldata senders) external override validateAuthorizedSenderSetter { + require(senders.length > 0, "Must have at least 1 authorized sender"); + // Set previous authorized senders to false + uint256 authorizedSendersLength = s_authorizedSenderList.length; + for (uint256 i = 0; i < authorizedSendersLength; i++) { + s_authorizedSenders[s_authorizedSenderList[i]] = false; + } + // Set new to true + for (uint256 i = 0; i < senders.length; i++) { + s_authorizedSenders[senders[i]] = true; + } + // Replace list + s_authorizedSenderList = senders; + emit AuthorizedSendersChanged(senders, msg.sender); + } + + /** + * @notice Retrieve a list of authorized senders + * @return array of addresses + */ + function getAuthorizedSenders() external view override returns (address[] memory) { + return s_authorizedSenderList; + } + + /** + * @notice Use this to check if a node is authorized for fulfilling requests + * @param sender The address of the Chainlink node + * @return The authorization status of the node + */ + function isAuthorizedSender(address sender) public view override returns (bool) { + return s_authorizedSenders[sender]; + } + + /** + * @notice customizable guard of who can update the authorized sender list + * @return bool whether sender can update authorized sender list + */ + function _canSetAuthorizedSenders() internal virtual returns (bool); + + /** + * @notice validates the sender is an authorized sender + */ + function _validateIsAuthorizedSender() internal view { + require(isAuthorizedSender(msg.sender), "Not authorized sender"); + } + + /** + * @notice prevents non-authorized addresses from calling this method + */ + modifier validateAuthorizedSender() { + _validateIsAuthorizedSender(); + _; + } + + /** + * @notice prevents non-authorized addresses from calling this method + */ + modifier validateAuthorizedSenderSetter() { + require(_canSetAuthorizedSenders(), "Cannot set authorized senders"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/Chainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/Chainlink.sol new file mode 100644 index 0000000..e08d87c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/Chainlink.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import {CBORChainlink} from "./vendor/CBORChainlink.sol"; +import {BufferChainlink} from "./vendor/BufferChainlink.sol"; + +/** + * @title Library for common Chainlink functions + * @dev Uses imported CBOR library for encoding to buffer + */ +library Chainlink { + uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase + + using CBORChainlink for BufferChainlink.buffer; + + struct Request { + bytes32 id; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + BufferChainlink.buffer buf; + } + + /** + * @notice Initializes a Chainlink request + * @dev Sets the ID, callback address, and callback function signature on the request + * @param self The uninitialized request + * @param jobId The Job Specification ID + * @param callbackAddr The callback address + * @param callbackFunc The callback function signature + * @return The initialized request + */ + function initialize( + Request memory self, + bytes32 jobId, + address callbackAddr, + bytes4 callbackFunc + ) internal pure returns (Chainlink.Request memory) { + BufferChainlink.init(self.buf, defaultBufferSize); + self.id = jobId; + self.callbackAddress = callbackAddr; + self.callbackFunctionId = callbackFunc; + return self; + } + + /** + * @notice Sets the data for the buffer without encoding CBOR on-chain + * @dev CBOR can be closed with curly-brackets {} or they can be left off + * @param self The initialized request + * @param data The CBOR data + */ + function setBuffer(Request memory self, bytes memory data) internal pure { + BufferChainlink.init(self.buf, data.length); + BufferChainlink.append(self.buf, data); + } + + /** + * @notice Adds a string value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The string value to add + */ + function add( + Request memory self, + string memory key, + string memory value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeString(value); + } + + /** + * @notice Adds a bytes value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The bytes value to add + */ + function addBytes( + Request memory self, + string memory key, + bytes memory value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeBytes(value); + } + + /** + * @notice Adds a int256 value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The int256 value to add + */ + function addInt( + Request memory self, + string memory key, + int256 value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeInt(value); + } + + /** + * @notice Adds a uint256 value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The uint256 value to add + */ + function addUint( + Request memory self, + string memory key, + uint256 value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeUInt(value); + } + + /** + * @notice Adds an array of strings to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param values The array of string values to add + */ + function addStringArray( + Request memory self, + string memory key, + string[] memory values + ) internal pure { + self.buf.encodeString(key); + self.buf.startArray(); + for (uint256 i = 0; i < values.length; i++) { + self.buf.encodeString(values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/ChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/ChainlinkClient.sol new file mode 100644 index 0000000..48cc048 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/ChainlinkClient.sol @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./Chainlink.sol"; +import "./interfaces/ENSInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/OperatorInterface.sol"; +import "./interfaces/PointerInterface.sol"; +import {ENSResolver as ENSResolver_Chainlink} from "./vendor/ENSResolver.sol"; + +/** + * @title The ChainlinkClient contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network + */ +abstract contract ChainlinkClient { + using Chainlink for Chainlink.Request; + + uint256 internal constant LINK_DIVISIBILITY = 10**18; + uint256 private constant AMOUNT_OVERRIDE = 0; + address private constant SENDER_OVERRIDE = address(0); + uint256 private constant ORACLE_ARGS_VERSION = 1; + uint256 private constant OPERATOR_ARGS_VERSION = 2; + bytes32 private constant ENS_TOKEN_SUBNAME = keccak256("link"); + bytes32 private constant ENS_ORACLE_SUBNAME = keccak256("oracle"); + address private constant LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; + + ENSInterface private s_ens; + bytes32 private s_ensNode; + LinkTokenInterface private s_link; + OperatorInterface private s_oracle; + uint256 private s_requestCount = 1; + mapping(bytes32 => address) private s_pendingRequests; + + event ChainlinkRequested(bytes32 indexed id); + event ChainlinkFulfilled(bytes32 indexed id); + event ChainlinkCancelled(bytes32 indexed id); + + /** + * @notice Creates a request that can hold additional parameters + * @param specId The Job Specification ID that the request will be created for + * @param callbackAddr address to operate the callback on + * @param callbackFunctionSignature function signature to use for the callback + * @return A Chainlink Request struct in memory + */ + function buildChainlinkRequest( + bytes32 specId, + address callbackAddr, + bytes4 callbackFunctionSignature + ) internal pure returns (Chainlink.Request memory) { + Chainlink.Request memory req; + return req.initialize(specId, callbackAddr, callbackFunctionSignature); + } + + /** + * @notice Creates a request that can hold additional parameters + * @param specId The Job Specification ID that the request will be created for + * @param callbackFunctionSignature function signature to use for the callback + * @return A Chainlink Request struct in memory + */ + function buildOperatorRequest(bytes32 specId, bytes4 callbackFunctionSignature) + internal + view + returns (Chainlink.Request memory) + { + Chainlink.Request memory req; + return req.initialize(specId, address(this), callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `chainlinkRequestTo` with the stored oracle address + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) { + return sendChainlinkRequestTo(address(s_oracle), req, payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequestTo( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment + ) internal returns (bytes32 requestId) { + uint256 nonce = s_requestCount; + s_requestCount = nonce + 1; + bytes memory encodedRequest = abi.encodeWithSelector( + ChainlinkRequestInterface.oracleRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + req.id, + address(this), + req.callbackFunctionId, + nonce, + ORACLE_ARGS_VERSION, + req.buf.buf + ); + return _rawRequest(oracleAddress, nonce, payment, encodedRequest); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev This function supports multi-word response + * @dev Calls `sendOperatorRequestTo` with the stored oracle address + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendOperatorRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) { + return sendOperatorRequestTo(address(s_oracle), req, payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev This function supports multi-word response + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendOperatorRequestTo( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment + ) internal returns (bytes32 requestId) { + uint256 nonce = s_requestCount; + s_requestCount = nonce + 1; + bytes memory encodedRequest = abi.encodeWithSelector( + OperatorInterface.operatorRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + req.id, + req.callbackFunctionId, + nonce, + OPERATOR_ARGS_VERSION, + req.buf.buf + ); + return _rawRequest(oracleAddress, nonce, payment, encodedRequest); + } + + /** + * @notice Make a request to an oracle + * @param oracleAddress The address of the oracle for the request + * @param nonce used to generate the request ID + * @param payment The amount of LINK to send for the request + * @param encodedRequest data encoded for request type specific format + * @return requestId The request ID + */ + function _rawRequest( + address oracleAddress, + uint256 nonce, + uint256 payment, + bytes memory encodedRequest + ) private returns (bytes32 requestId) { + requestId = keccak256(abi.encodePacked(this, nonce)); + s_pendingRequests[requestId] = oracleAddress; + emit ChainlinkRequested(requestId); + require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle"); + } + + /** + * @notice Allows a request to be cancelled if it has not been fulfilled + * @dev Requires keeping track of the expiration value emitted from the oracle contract. + * Deletes the request from the `pendingRequests` mapping. + * Emits ChainlinkCancelled event. + * @param requestId The request ID + * @param payment The amount of LINK sent for the request + * @param callbackFunc The callback function specified for the request + * @param expiration The time of the expiration for the request + */ + function cancelChainlinkRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunc, + uint256 expiration + ) internal { + OperatorInterface requested = OperatorInterface(s_pendingRequests[requestId]); + delete s_pendingRequests[requestId]; + emit ChainlinkCancelled(requestId); + requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration); + } + + /** + * @notice the next request count to be used in generating a nonce + * @dev starts at 1 in order to ensure consistent gas cost + * @return returns the next request count to be used in a nonce + */ + function getNextRequestCount() internal view returns (uint256) { + return s_requestCount; + } + + /** + * @notice Sets the stored oracle address + * @param oracleAddress The address of the oracle contract + */ + function setChainlinkOracle(address oracleAddress) internal { + s_oracle = OperatorInterface(oracleAddress); + } + + /** + * @notice Sets the LINK token address + * @param linkAddress The address of the LINK token contract + */ + function setChainlinkToken(address linkAddress) internal { + s_link = LinkTokenInterface(linkAddress); + } + + /** + * @notice Sets the Chainlink token address for the public + * network as given by the Pointer contract + */ + function setPublicChainlinkToken() internal { + setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkTokenAddress() internal view returns (address) { + return address(s_link); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function chainlinkOracleAddress() internal view returns (address) { + return address(s_oracle); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param oracleAddress The address of the oracle contract that will fulfill the request + * @param requestId The request ID used for the response + */ + function addChainlinkExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) { + s_pendingRequests[requestId] = oracleAddress; + } + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param ensAddress The address of the ENS contract + * @param node The ENS node hash + */ + function useChainlinkWithENS(address ensAddress, bytes32 node) internal { + s_ens = ENSInterface(ensAddress); + s_ensNode = node; + bytes32 linkSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_TOKEN_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(linkSubnode)); + setChainlinkToken(resolver.addr(linkSubnode)); + updateChainlinkOracleWithENS(); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously + */ + function updateChainlinkOracleWithENS() internal { + bytes32 oracleSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_ORACLE_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(oracleSubnode)); + setChainlinkOracle(resolver.addr(oracleSubnode)); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param requestId The request ID for fulfillment + */ + function validateChainlinkCallback(bytes32 requestId) + internal + recordChainlinkFulfillment(requestId) + // solhint-disable-next-line no-empty-blocks + { + + } + + /** + * @dev Reverts if the sender is not the oracle of the request. + * Emits ChainlinkFulfilled event. + * @param requestId The request ID for fulfillment + */ + modifier recordChainlinkFulfillment(bytes32 requestId) { + require(msg.sender == s_pendingRequests[requestId], "Source must be the oracle of the request"); + delete s_pendingRequests[requestId]; + emit ChainlinkFulfilled(requestId); + _; + } + + /** + * @dev Reverts if the request is already pending + * @param requestId The request ID for fulfillment + */ + modifier notPendingRequest(bytes32 requestId) { + require(s_pendingRequests[requestId] == address(0), "Request is already pending"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/ConfirmedOwner.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/ConfirmedOwner.sol new file mode 100644 index 0000000..a411ba8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/ConfirmedOwner.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./ConfirmedOwnerWithProposal.sol"; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwner is ConfirmedOwnerWithProposal { + constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/ConfirmedOwnerWithProposal.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/ConfirmedOwnerWithProposal.sol new file mode 100644 index 0000000..b95c171 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/ConfirmedOwnerWithProposal.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interfaces/OwnableInterface.sol"; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwnerWithProposal is OwnableInterface { + address private s_owner; + address private s_pendingOwner; + + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + + constructor(address newOwner, address pendingOwner) { + require(newOwner != address(0), "Cannot set owner to zero"); + + s_owner = newOwner; + if (pendingOwner != address(0)) { + _transferOwnership(pendingOwner); + } + } + + /** + * @notice Allows an owner to begin transferring ownership to a new address, + * pending. + */ + function transferOwnership(address to) public override onlyOwner { + _transferOwnership(to); + } + + /** + * @notice Allows an ownership transfer to be completed by the recipient. + */ + function acceptOwnership() external override { + require(msg.sender == s_pendingOwner, "Must be proposed owner"); + + address oldOwner = s_owner; + s_owner = msg.sender; + s_pendingOwner = address(0); + + emit OwnershipTransferred(oldOwner, msg.sender); + } + + /** + * @notice Get the current owner + */ + function owner() public view override returns (address) { + return s_owner; + } + + /** + * @notice validate, transfer ownership, and emit relevant events + */ + function _transferOwnership(address to) private { + require(to != msg.sender, "Cannot transfer to self"); + + s_pendingOwner = to; + + emit OwnershipTransferRequested(s_owner, to); + } + + /** + * @notice validate access + */ + function _validateOwnership() internal view { + require(msg.sender == s_owner, "Only callable by owner"); + } + + /** + * @notice Reverts if called by anyone other than the contract owner. + */ + modifier onlyOwner() { + _validateOwnership(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/Denominations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/Denominations.sol new file mode 100644 index 0000000..54556db --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/Denominations.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +library Denominations { + address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 + address public constant USD = address(840); + address public constant GBP = address(826); + address public constant EUR = address(978); + address public constant JPY = address(392); + address public constant KRW = address(410); + address public constant CNY = address(156); + address public constant AUD = address(36); + address public constant CAD = address(124); + address public constant CHF = address(756); + address public constant ARS = address(32); + address public constant PHP = address(608); + address public constant NZD = address(554); + address public constant SGD = address(702); + address public constant NGN = address(566); + address public constant ZAR = address(710); + address public constant RUB = address(643); + address public constant INR = address(356); + address public constant BRL = address(986); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperBase.sol new file mode 100644 index 0000000..6af11a8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperBase.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract KeeperBase { + /** + * @notice method that allows it to be simulated via eth_call by checking that + * the sender is the zero address. + */ + function preventExecution() internal view { + require(tx.origin == address(0), "only for simulated backend"); + } + + /** + * @notice modifier that allows it to be simulated via eth_call by checking + * that the sender is the zero address. + */ + modifier cannotExecute() { + preventExecution(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperCompatible.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperCompatible.sol new file mode 100644 index 0000000..9780eb6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperCompatible.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./KeeperBase.sol"; +import "./interfaces/KeeperCompatibleInterface.sol"; + +abstract contract KeeperCompatible is KeeperBase, KeeperCompatibleInterface {} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperRegistry.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperRegistry.sol new file mode 100644 index 0000000..f0ab21e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperRegistry.sol @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interfaces/AggregatorV3Interface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperCompatibleInterface.sol"; +import "./interfaces/KeeperRegistryInterface.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./vendor/SafeMathChainlink.sol"; +import "./vendor/Address.sol"; +import "./vendor/Pausable.sol"; +import "./vendor/ReentrancyGuard.sol"; +import "./vendor/SignedSafeMath.sol"; +import "./vendor/SafeMath96.sol"; +import "./KeeperBase.sol"; +import "./ConfirmedOwner.sol"; + +/** + * @notice Registry for adding work for Chainlink Keepers to perform on client + * contracts. Clients must support the Upkeep interface. + */ +contract KeeperRegistry is + TypeAndVersionInterface, + ConfirmedOwner, + KeeperBase, + ReentrancyGuard, + Pausable, + KeeperRegistryExecutableInterface +{ + using Address for address; + using SafeMathChainlink for uint256; + using SafeMath96 for uint96; + using SignedSafeMath for int256; + + address private constant ZERO_ADDRESS = address(0); + address private constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 private constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 private constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + uint256 private constant CALL_GAS_MAX = 5_000_000; + uint256 private constant CALL_GAS_MIN = 2_300; + uint256 private constant CANCELATION_DELAY = 50; + uint256 private constant CUSHION = 5_000; + uint256 private constant REGISTRY_GAS_OVERHEAD = 80_000; + uint256 private constant PPB_BASE = 1_000_000_000; + uint64 private constant UINT64_MAX = 2**64 - 1; + uint96 private constant LINK_TOTAL_SUPPLY = 1e27; + + uint256 private s_upkeepCount; + uint256[] private s_canceledUpkeepList; + address[] private s_keeperList; + mapping(uint256 => Upkeep) private s_upkeep; + mapping(address => KeeperInfo) private s_keeperInfo; + mapping(address => address) private s_proposedPayee; + mapping(uint256 => bytes) private s_checkData; + Config private s_config; + uint256 private s_fallbackGasPrice; // not in config object for gas savings + uint256 private s_fallbackLinkPrice; // not in config object for gas savings + uint256 private s_expectedLinkBalance; + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + AggregatorV3Interface public immutable FAST_GAS_FEED; + + address private s_registrar; + + /** + * @notice versions: + * - KeeperRegistry 1.1.0: added flatFeeMicroLink + * - KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistry 1.1.0"; + + struct Upkeep { + address target; + uint32 executeGas; + uint96 balance; + address admin; + uint64 maxValidBlocknumber; + address lastKeeper; + } + + struct KeeperInfo { + address payee; + uint96 balance; + bool active; + } + + struct Config { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + } + + struct PerformParams { + address from; + uint256 id; + bytes performData; + uint256 maxLinkPayment; + uint256 gasLimit; + uint256 adjustedGasWei; + uint256 linkEth; + } + + event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + address indexed from, + uint96 payment, + bytes performData + ); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event ConfigSet( + uint32 paymentPremiumPPB, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ); + event FlatFeeSet(uint32 flatFeeMicroLink); + event KeepersUpdated(address[] keepers, address[] payees); + event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); + event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); + event RegistrarChanged(address indexed from, address indexed to); + + /** + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + * @param paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @param flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @param blockCountPerTurn number of blocks each oracle has during their turn to + * perform upkeep before it will be the next keeper's turn to submit + * @param checkGasLimit gas limit when checking for upkeep + * @param stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @param gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @param fallbackGasPrice gas price used if the gas price feed is stale + * @param fallbackLinkPrice LINK price used if the LINK price feed is stale + */ + constructor( + address link, + address linkEthFeed, + address fastGasFeed, + uint32 paymentPremiumPPB, + uint32 flatFeeMicroLink, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); + + setConfig( + paymentPremiumPPB, + flatFeeMicroLink, + blockCountPerTurn, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + fallbackGasPrice, + fallbackLinkPrice + ); + } + + // ACTIONS + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external override onlyOwnerOrRegistrar returns (uint256 id) { + require(target.isContract(), "target is not a contract"); + require(gasLimit >= CALL_GAS_MIN, "min gas is 2300"); + require(gasLimit <= CALL_GAS_MAX, "max gas is 5000000"); + + id = s_upkeepCount; + s_upkeep[id] = Upkeep({ + target: target, + executeGas: gasLimit, + balance: 0, + admin: admin, + maxValidBlocknumber: UINT64_MAX, + lastKeeper: address(0) + }); + s_checkData[id] = checkData; + s_upkeepCount++; + + emit UpkeepRegistered(id, gasLimit, admin); + + return id; + } + + /** + * @notice simulated by keepers via eth_call to see if the upkeep needs to be + * performed. If upkeep is needed, the call then simulates performUpkeep + * to make sure it succeeds. Finally, it returns the success status along with + * payment information and the perform data payload. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function checkUpkeep(uint256 id, address from) + external + override + whenNotPaused + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + Upkeep memory upkeep = s_upkeep[id]; + + bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); + (bool success, bytes memory result) = upkeep.target.call{gas: s_config.checkGasLimit}(callData); + + if (!success) { + string memory upkeepRevertReason = getRevertMsg(result); + string memory reason = string(abi.encodePacked("call to check target failed: ", upkeepRevertReason)); + revert(reason); + } + + (success, performData) = abi.decode(result, (bool, bytes)); + require(success, "upkeep not needed"); + + PerformParams memory params = generatePerformParams(from, id, performData, false); + prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + return (performData, params.maxLinkPayment, params.gasLimit, params.adjustedGasWei, params.linkEth); + } + + /** + * @notice executes the upkeep with the perform data returned from + * checkUpkeep, validates the keeper's permissions, and pays the keeper. + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + */ + function performUpkeep(uint256 id, bytes calldata performData) external override returns (bool success) { + return performUpkeepWithParams(generatePerformParams(msg.sender, id, performData, true)); + } + + /** + * @notice prevent an upkeep from being performed in the future + * @param id upkeep to be canceled + */ + function cancelUpkeep(uint256 id) external override { + uint64 maxValid = s_upkeep[id].maxValidBlocknumber; + bool notCanceled = maxValid == UINT64_MAX; + bool isOwner = msg.sender == owner(); + require(notCanceled || (isOwner && maxValid > block.number), "too late to cancel upkeep"); + require(isOwner || msg.sender == s_upkeep[id].admin, "only owner or admin"); + + uint256 height = block.number; + if (!isOwner) { + height = height.add(CANCELATION_DELAY); + } + s_upkeep[id].maxValidBlocknumber = uint64(height); + if (notCanceled) { + s_canceledUpkeepList.push(id); + } + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @notice adds LINK funding for an upkeep by transferring from the sender's + * LINK balance + * @param id upkeep to fund + * @param amount number of LINK to transfer + */ + function addFunds(uint256 id, uint96 amount) external override { + require(s_upkeep[id].maxValidBlocknumber == UINT64_MAX, "upkeep must be active"); + s_upkeep[id].balance = s_upkeep[id].balance.add(amount); + s_expectedLinkBalance = s_expectedLinkBalance.add(amount); + LINK.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external { + require(msg.sender == address(LINK), "only callable through LINK"); + require(data.length == 32, "data must be 32 bytes"); + uint256 id = abi.decode(data, (uint256)); + require(s_upkeep[id].maxValidBlocknumber == UINT64_MAX, "upkeep must be active"); + + s_upkeep[id].balance = s_upkeep[id].balance.add(uint96(amount)); + s_expectedLinkBalance = s_expectedLinkBalance.add(amount); + + emit FundsAdded(id, sender, uint96(amount)); + } + + /** + * @notice removes funding from a canceled upkeep + * @param id upkeep to withdraw funds from + * @param to destination address for sending remaining funds + */ + function withdrawFunds(uint256 id, address to) external validateRecipient(to) { + require(s_upkeep[id].admin == msg.sender, "only callable by admin"); + require(s_upkeep[id].maxValidBlocknumber <= block.number, "upkeep must be canceled"); + + uint256 amount = s_upkeep[id].balance; + s_upkeep[id].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance.sub(amount); + emit FundsWithdrawn(id, amount, to); + + LINK.transfer(to, amount); + } + + /** + * @notice recovers LINK funds improperly transferred to the registry + * @dev In principle this function’s execution cost could exceed block + * gas limit. However, in our anticipated deployment, the number of upkeeps and + * keepers will be low enough to avoid this problem. + */ + function recoverFunds() external onlyOwner { + uint256 total = LINK.balanceOf(address(this)); + LINK.transfer(msg.sender, total.sub(s_expectedLinkBalance)); + } + + /** + * @notice withdraws a keeper's payment, callable only by the keeper's payee + * @param from keeper address + * @param to address to send the payment to + */ + function withdrawPayment(address from, address to) external validateRecipient(to) { + KeeperInfo memory keeper = s_keeperInfo[from]; + require(keeper.payee == msg.sender, "only callable by payee"); + + s_keeperInfo[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance.sub(keeper.balance); + emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); + + LINK.transfer(to, keeper.balance); + } + + /** + * @notice proposes the safe transfer of a keeper's payee to another address + * @param keeper address of the keeper to transfer payee role + * @param proposed address to nominate for next payeeship + */ + function transferPayeeship(address keeper, address proposed) external { + require(s_keeperInfo[keeper].payee == msg.sender, "only callable by payee"); + require(proposed != msg.sender, "cannot transfer to self"); + + if (s_proposedPayee[keeper] != proposed) { + s_proposedPayee[keeper] = proposed; + emit PayeeshipTransferRequested(keeper, msg.sender, proposed); + } + } + + /** + * @notice accepts the safe transfer of payee role for a keeper + * @param keeper address to accept the payee role for + */ + function acceptPayeeship(address keeper) external { + require(s_proposedPayee[keeper] == msg.sender, "only callable by proposed payee"); + address past = s_keeperInfo[keeper].payee; + s_keeperInfo[keeper].payee = msg.sender; + s_proposedPayee[keeper] = ZERO_ADDRESS; + + emit PayeeshipTransferred(keeper, past, msg.sender); + } + + /** + * @notice signals to keepers that they should not perform upkeeps until the + * contract has been unpaused + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice signals to keepers that they can perform upkeeps once again after + * having been paused + */ + function unpause() external onlyOwner { + _unpause(); + } + + // SETTERS + + /** + * @notice updates the configuration of the registry + * @param paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @param flatFeeMicroLink flat fee paid to oracles for performing upkeeps + * @param blockCountPerTurn number of blocks an oracle should wait before + * checking for upkeep + * @param checkGasLimit gas limit when checking for upkeep + * @param stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @param fallbackGasPrice gas price used if the gas price feed is stale + * @param fallbackLinkPrice LINK price used if the LINK price feed is stale + */ + function setConfig( + uint32 paymentPremiumPPB, + uint32 flatFeeMicroLink, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ) public onlyOwner { + s_config = Config({ + paymentPremiumPPB: paymentPremiumPPB, + flatFeeMicroLink: flatFeeMicroLink, + blockCountPerTurn: blockCountPerTurn, + checkGasLimit: checkGasLimit, + stalenessSeconds: stalenessSeconds, + gasCeilingMultiplier: gasCeilingMultiplier + }); + s_fallbackGasPrice = fallbackGasPrice; + s_fallbackLinkPrice = fallbackLinkPrice; + + emit ConfigSet( + paymentPremiumPPB, + blockCountPerTurn, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + fallbackGasPrice, + fallbackLinkPrice + ); + emit FlatFeeSet(flatFeeMicroLink); + } + + /** + * @notice update the list of keepers allowed to perform upkeep + * @param keepers list of addresses allowed to perform upkeep + * @param payees addresses corresponding to keepers who are allowed to + * move payments which have been accrued + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { + require(keepers.length == payees.length, "address lists not the same length"); + require(keepers.length >= 2, "not enough keepers"); + for (uint256 i = 0; i < s_keeperList.length; i++) { + address keeper = s_keeperList[i]; + s_keeperInfo[keeper].active = false; + } + for (uint256 i = 0; i < keepers.length; i++) { + address keeper = keepers[i]; + KeeperInfo storage s_keeper = s_keeperInfo[keeper]; + address oldPayee = s_keeper.payee; + address newPayee = payees[i]; + require(newPayee != address(0), "cannot set payee to the zero address"); + require(oldPayee == ZERO_ADDRESS || oldPayee == newPayee || newPayee == IGNORE_ADDRESS, "cannot change payee"); + require(!s_keeper.active, "cannot add keeper twice"); + s_keeper.active = true; + if (newPayee != IGNORE_ADDRESS) { + s_keeper.payee = newPayee; + } + } + s_keeperList = keepers; + emit KeepersUpdated(keepers, payees); + } + + /** + * @notice update registrar + * @param registrar new registrar + */ + function setRegistrar(address registrar) external onlyOwnerOrRegistrar { + address previous = s_registrar; + require(registrar != previous, "Same registrar"); + s_registrar = registrar; + emit RegistrarChanged(previous, registrar); + } + + // GETTERS + + /** + * @notice read all of the details about an upkeep + */ + function getUpkeep(uint256 id) + external + view + override + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber + ) + { + Upkeep memory reg = s_upkeep[id]; + return ( + reg.target, + reg.executeGas, + s_checkData[id], + reg.balance, + reg.lastKeeper, + reg.admin, + reg.maxValidBlocknumber + ); + } + + /** + * @notice read the total number of upkeep's registered + */ + function getUpkeepCount() external view override returns (uint256) { + return s_upkeepCount; + } + + /** + * @notice read the current list canceled upkeep IDs + */ + function getCanceledUpkeepList() external view override returns (uint256[] memory) { + return s_canceledUpkeepList; + } + + /** + * @notice read the current list of addresses allowed to perform upkeep + */ + function getKeeperList() external view override returns (address[] memory) { + return s_keeperList; + } + + /** + * @notice read the current registrar + */ + function getRegistrar() external view returns (address) { + return s_registrar; + } + + /** + * @notice read the current info about any keeper address + */ + function getKeeperInfo(address query) + external + view + override + returns ( + address payee, + bool active, + uint96 balance + ) + { + KeeperInfo memory keeper = s_keeperInfo[query]; + return (keeper.payee, keeper.active, keeper.balance); + } + + /** + * @notice read the current configuration of the registry + */ + function getConfig() + external + view + override + returns ( + uint32 paymentPremiumPPB, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ) + { + Config memory config = s_config; + return ( + config.paymentPremiumPPB, + config.blockCountPerTurn, + config.checkGasLimit, + config.stalenessSeconds, + config.gasCeilingMultiplier, + s_fallbackGasPrice, + s_fallbackLinkPrice + ); + } + + /** + * @notice getFlatFee gets the flat rate fee charged to customers when performing upkeep, + * in units of of micro LINK + */ + function getFlatFee() external view returns (uint32) { + return s_config.flatFeeMicroLink; + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + */ + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { + return getMaxPaymentForGas(s_upkeep[id].executeGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + */ + function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { + (uint256 gasWei, uint256 linkEth) = getFeedData(); + uint256 adjustedGasWei = adjustGasPrice(gasWei, false); + return calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + } + + // PRIVATE + + /** + * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function getFeedData() private view returns (uint256 gasWei, uint256 linkEth) { + uint32 stalenessSeconds = s_config.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + linkEth = s_fallbackLinkPrice; + } else { + linkEth = uint256(feedValue); + } + return (gasWei, linkEth); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + */ + function calculatePaymentAmount( + uint256 gasLimit, + uint256 gasWei, + uint256 linkEth + ) private view returns (uint96 payment) { + Config memory config = s_config; + uint256 weiForGas = gasWei.mul(gasLimit.add(REGISTRY_GAS_OVERHEAD)); + uint256 premium = PPB_BASE.add(config.paymentPremiumPPB); + uint256 total = weiForGas.mul(1e9).mul(premium).div(linkEth).add(uint256(config.flatFeeMicroLink).mul(1e12)); + require(total <= LINK_TOTAL_SUPPLY, "payment greater than all LINK"); + return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available + */ + function callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= CUSHION and check for underflow + if lt(g, CUSHION) { + revert(0, 0) + } + g := sub(g, CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * keeper and the exact gas required by the Upkeep + */ + function performUpkeepWithParams(PerformParams memory params) + private + nonReentrant + validUpkeep(params.id) + returns (bool success) + { + Upkeep memory upkeep = s_upkeep[params.id]; + prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + uint256 gasUsed = gasleft(); + bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); + success = callWithExactGas(params.gasLimit, upkeep.target, callData); + gasUsed = gasUsed - gasleft(); + + uint96 payment = calculatePaymentAmount(gasUsed, params.adjustedGasWei, params.linkEth); + upkeep.balance = upkeep.balance.sub(payment); + upkeep.lastKeeper = params.from; + s_upkeep[params.id] = upkeep; + uint96 newBalance = s_keeperInfo[params.from].balance.add(payment); + s_keeperInfo[params.from].balance = newBalance; + + emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); + return success; + } + + /** + * @dev ensures a upkeep is valid + */ + function validateUpkeep(uint256 id) private view { + require(s_upkeep[id].maxValidBlocknumber > block.number, "invalid upkeep id"); + } + + /** + * @dev ensures all required checks are passed before an upkeep is performed + */ + function prePerformUpkeep( + Upkeep memory upkeep, + address from, + uint256 maxLinkPayment + ) private view { + require(s_keeperInfo[from].active, "only active keepers"); + require(upkeep.balance >= maxLinkPayment, "insufficient funds"); + require(upkeep.lastKeeper != from, "keepers must take turns"); + } + + /** + * @dev adjusts the gas price to min(ceiling, tx.gasprice) or just uses the ceiling if tx.gasprice is disabled + */ + function adjustGasPrice(uint256 gasWei, bool useTxGasPrice) private view returns (uint256 adjustedPrice) { + adjustedPrice = gasWei.mul(s_config.gasCeilingMultiplier); + if (useTxGasPrice && tx.gasprice < adjustedPrice) { + adjustedPrice = tx.gasprice; + } + } + + /** + * @dev generates a PerformParams struct for use in performUpkeepWithParams() + */ + function generatePerformParams( + address from, + uint256 id, + bytes memory performData, + bool useTxGasPrice + ) private view returns (PerformParams memory) { + uint256 gasLimit = s_upkeep[id].executeGas; + (uint256 gasWei, uint256 linkEth) = getFeedData(); + uint256 adjustedGasWei = adjustGasPrice(gasWei, useTxGasPrice); + uint96 maxLinkPayment = calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + + return + PerformParams({ + from: from, + id: id, + performData: performData, + maxLinkPayment: maxLinkPayment, + gasLimit: gasLimit, + adjustedGasWei: adjustedGasWei, + linkEth: linkEth + }); + } + + /** + * @dev extracts a revert reason from a call result payload + */ + function getRevertMsg(bytes memory _payload) private pure returns (string memory) { + if (_payload.length < 68) return "transaction reverted silently"; + assembly { + _payload := add(_payload, 0x04) + } + return abi.decode(_payload, (string)); + } + + // MODIFIERS + + /** + * @dev ensures a upkeep is valid + */ + modifier validUpkeep(uint256 id) { + validateUpkeep(id); + _; + } + + /** + * @dev ensures that burns don't accidentally happen by sending to the zero + * address + */ + modifier validateRecipient(address to) { + require(to != address(0), "cannot send to zero address"); + _; + } + + /** + * @dev Reverts if called by anyone other than the contract owner or registrar. + */ + modifier onlyOwnerOrRegistrar() { + require(msg.sender == owner() || msg.sender == s_registrar, "Only callable by owner or registrar"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperRegistry1_1.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperRegistry1_1.sol new file mode 100644 index 0000000..d61ada8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/KeeperRegistry1_1.sol @@ -0,0 +1,834 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interfaces/AggregatorV3Interface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperCompatibleInterface.sol"; +import "./interfaces/KeeperRegistryInterface.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./vendor/SafeMathChainlink.sol"; +import "./vendor/Address.sol"; +import "./vendor/Pausable.sol"; +import "./vendor/ReentrancyGuard.sol"; +import "./vendor/SignedSafeMath.sol"; +import "./vendor/SafeMath96.sol"; +import "./KeeperBase.sol"; +import "./ConfirmedOwner.sol"; + +/** + * @notice Registry for adding work for Chainlink Keepers to perform on client + * contracts. Clients must support the Upkeep interface. + */ +contract KeeperRegistry1_1 is + TypeAndVersionInterface, + ConfirmedOwner, + KeeperBase, + ReentrancyGuard, + Pausable, + KeeperRegistryExecutableInterface +{ + using Address for address; + using SafeMathChainlink for uint256; + using SafeMath96 for uint96; + using SignedSafeMath for int256; + + address private constant ZERO_ADDRESS = address(0); + address private constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 private constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 private constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + uint256 private constant CALL_GAS_MAX = 5_000_000; + uint256 private constant CALL_GAS_MIN = 2_300; + uint256 private constant CANCELATION_DELAY = 50; + uint256 private constant CUSHION = 5_000; + uint256 private constant REGISTRY_GAS_OVERHEAD = 80_000; + uint256 private constant PPB_BASE = 1_000_000_000; + uint64 private constant UINT64_MAX = 2**64 - 1; + uint96 private constant LINK_TOTAL_SUPPLY = 1e27; + + uint256 private s_upkeepCount; + uint256[] private s_canceledUpkeepList; + address[] private s_keeperList; + mapping(uint256 => Upkeep) private s_upkeep; + mapping(address => KeeperInfo) private s_keeperInfo; + mapping(address => address) private s_proposedPayee; + mapping(uint256 => bytes) private s_checkData; + Config private s_config; + uint256 private s_fallbackGasPrice; // not in config object for gas savings + uint256 private s_fallbackLinkPrice; // not in config object for gas savings + uint256 private s_expectedLinkBalance; + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + AggregatorV3Interface public immutable FAST_GAS_FEED; + + address private s_registrar; + + /** + * @notice versions: + * - KeeperRegistry 1.1.0: added flatFeeMicroLink + * - KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistry 1.1.0"; + + struct Upkeep { + address target; + uint32 executeGas; + uint96 balance; + address admin; + uint64 maxValidBlocknumber; + address lastKeeper; + } + + struct KeeperInfo { + address payee; + uint96 balance; + bool active; + } + + struct Config { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + } + + struct PerformParams { + address from; + uint256 id; + bytes performData; + uint256 maxLinkPayment; + uint256 gasLimit; + uint256 adjustedGasWei; + uint256 linkEth; + } + + event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + address indexed from, + uint96 payment, + bytes performData + ); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event ConfigSet( + uint32 paymentPremiumPPB, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ); + event FlatFeeSet(uint32 flatFeeMicroLink); + event KeepersUpdated(address[] keepers, address[] payees); + event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); + event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); + event RegistrarChanged(address indexed from, address indexed to); + + /** + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + * @param paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @param flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @param blockCountPerTurn number of blocks each oracle has during their turn to + * perform upkeep before it will be the next keeper's turn to submit + * @param checkGasLimit gas limit when checking for upkeep + * @param stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @param gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @param fallbackGasPrice gas price used if the gas price feed is stale + * @param fallbackLinkPrice LINK price used if the LINK price feed is stale + */ + constructor( + address link, + address linkEthFeed, + address fastGasFeed, + uint32 paymentPremiumPPB, + uint32 flatFeeMicroLink, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); + + setConfig( + paymentPremiumPPB, + flatFeeMicroLink, + blockCountPerTurn, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + fallbackGasPrice, + fallbackLinkPrice + ); + } + + // ACTIONS + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external override onlyOwnerOrRegistrar returns (uint256 id) { + require(target.isContract(), "target is not a contract"); + require(gasLimit >= CALL_GAS_MIN, "min gas is 2300"); + require(gasLimit <= CALL_GAS_MAX, "max gas is 5000000"); + + id = s_upkeepCount; + s_upkeep[id] = Upkeep({ + target: target, + executeGas: gasLimit, + balance: 0, + admin: admin, + maxValidBlocknumber: UINT64_MAX, + lastKeeper: address(0) + }); + s_checkData[id] = checkData; + s_upkeepCount++; + + emit UpkeepRegistered(id, gasLimit, admin); + + return id; + } + + /** + * @notice simulated by keepers via eth_call to see if the upkeep needs to be + * performed. If upkeep is needed, the call then simulates performUpkeep + * to make sure it succeeds. Finally, it returns the success status along with + * payment information and the perform data payload. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function checkUpkeep(uint256 id, address from) + external + override + whenNotPaused + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + Upkeep memory upkeep = s_upkeep[id]; + + bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); + (bool success, bytes memory result) = upkeep.target.call{gas: s_config.checkGasLimit}(callData); + + if (!success) { + string memory upkeepRevertReason = getRevertMsg(result); + string memory reason = string(abi.encodePacked("call to check target failed: ", upkeepRevertReason)); + revert(reason); + } + + (success, performData) = abi.decode(result, (bool, bytes)); + require(success, "upkeep not needed"); + + PerformParams memory params = generatePerformParams(from, id, performData, false); + prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + return (performData, params.maxLinkPayment, params.gasLimit, params.adjustedGasWei, params.linkEth); + } + + /** + * @notice executes the upkeep with the perform data returned from + * checkUpkeep, validates the keeper's permissions, and pays the keeper. + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + */ + function performUpkeep(uint256 id, bytes calldata performData) external override returns (bool success) { + return performUpkeepWithParams(generatePerformParams(msg.sender, id, performData, true)); + } + + /** + * @notice prevent an upkeep from being performed in the future + * @param id upkeep to be canceled + */ + function cancelUpkeep(uint256 id) external override { + uint64 maxValid = s_upkeep[id].maxValidBlocknumber; + bool notCanceled = maxValid == UINT64_MAX; + bool isOwner = msg.sender == owner(); + require(notCanceled || (isOwner && maxValid > block.number), "too late to cancel upkeep"); + require(isOwner || msg.sender == s_upkeep[id].admin, "only owner or admin"); + + uint256 height = block.number; + if (!isOwner) { + height = height.add(CANCELATION_DELAY); + } + s_upkeep[id].maxValidBlocknumber = uint64(height); + if (notCanceled) { + s_canceledUpkeepList.push(id); + } + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @notice adds LINK funding for an upkeep by transferring from the sender's + * LINK balance + * @param id upkeep to fund + * @param amount number of LINK to transfer + */ + function addFunds(uint256 id, uint96 amount) external override { + require(s_upkeep[id].maxValidBlocknumber == UINT64_MAX, "upkeep must be active"); + s_upkeep[id].balance = s_upkeep[id].balance.add(amount); + s_expectedLinkBalance = s_expectedLinkBalance.add(amount); + LINK.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external { + require(msg.sender == address(LINK), "only callable through LINK"); + require(data.length == 32, "data must be 32 bytes"); + uint256 id = abi.decode(data, (uint256)); + require(s_upkeep[id].maxValidBlocknumber == UINT64_MAX, "upkeep must be active"); + + s_upkeep[id].balance = s_upkeep[id].balance.add(uint96(amount)); + s_expectedLinkBalance = s_expectedLinkBalance.add(amount); + + emit FundsAdded(id, sender, uint96(amount)); + } + + /** + * @notice removes funding from a canceled upkeep + * @param id upkeep to withdraw funds from + * @param to destination address for sending remaining funds + */ + function withdrawFunds(uint256 id, address to) external validateRecipient(to) { + require(s_upkeep[id].admin == msg.sender, "only callable by admin"); + require(s_upkeep[id].maxValidBlocknumber <= block.number, "upkeep must be canceled"); + + uint256 amount = s_upkeep[id].balance; + s_upkeep[id].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance.sub(amount); + emit FundsWithdrawn(id, amount, to); + + LINK.transfer(to, amount); + } + + /** + * @notice recovers LINK funds improperly transferred to the registry + * @dev In principle this function’s execution cost could exceed block + * gas limit. However, in our anticipated deployment, the number of upkeeps and + * keepers will be low enough to avoid this problem. + */ + function recoverFunds() external onlyOwner { + uint256 total = LINK.balanceOf(address(this)); + LINK.transfer(msg.sender, total.sub(s_expectedLinkBalance)); + } + + /** + * @notice withdraws a keeper's payment, callable only by the keeper's payee + * @param from keeper address + * @param to address to send the payment to + */ + function withdrawPayment(address from, address to) external validateRecipient(to) { + KeeperInfo memory keeper = s_keeperInfo[from]; + require(keeper.payee == msg.sender, "only callable by payee"); + + s_keeperInfo[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance.sub(keeper.balance); + emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); + + LINK.transfer(to, keeper.balance); + } + + /** + * @notice proposes the safe transfer of a keeper's payee to another address + * @param keeper address of the keeper to transfer payee role + * @param proposed address to nominate for next payeeship + */ + function transferPayeeship(address keeper, address proposed) external { + require(s_keeperInfo[keeper].payee == msg.sender, "only callable by payee"); + require(proposed != msg.sender, "cannot transfer to self"); + + if (s_proposedPayee[keeper] != proposed) { + s_proposedPayee[keeper] = proposed; + emit PayeeshipTransferRequested(keeper, msg.sender, proposed); + } + } + + /** + * @notice accepts the safe transfer of payee role for a keeper + * @param keeper address to accept the payee role for + */ + function acceptPayeeship(address keeper) external { + require(s_proposedPayee[keeper] == msg.sender, "only callable by proposed payee"); + address past = s_keeperInfo[keeper].payee; + s_keeperInfo[keeper].payee = msg.sender; + s_proposedPayee[keeper] = ZERO_ADDRESS; + + emit PayeeshipTransferred(keeper, past, msg.sender); + } + + /** + * @notice signals to keepers that they should not perform upkeeps until the + * contract has been unpaused + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice signals to keepers that they can perform upkeeps once again after + * having been paused + */ + function unpause() external onlyOwner { + _unpause(); + } + + // SETTERS + + /** + * @notice updates the configuration of the registry + * @param paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @param flatFeeMicroLink flat fee paid to oracles for performing upkeeps + * @param blockCountPerTurn number of blocks an oracle should wait before + * checking for upkeep + * @param checkGasLimit gas limit when checking for upkeep + * @param stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @param fallbackGasPrice gas price used if the gas price feed is stale + * @param fallbackLinkPrice LINK price used if the LINK price feed is stale + */ + function setConfig( + uint32 paymentPremiumPPB, + uint32 flatFeeMicroLink, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ) public onlyOwner { + s_config = Config({ + paymentPremiumPPB: paymentPremiumPPB, + flatFeeMicroLink: flatFeeMicroLink, + blockCountPerTurn: blockCountPerTurn, + checkGasLimit: checkGasLimit, + stalenessSeconds: stalenessSeconds, + gasCeilingMultiplier: gasCeilingMultiplier + }); + s_fallbackGasPrice = fallbackGasPrice; + s_fallbackLinkPrice = fallbackLinkPrice; + + emit ConfigSet( + paymentPremiumPPB, + blockCountPerTurn, + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + fallbackGasPrice, + fallbackLinkPrice + ); + emit FlatFeeSet(flatFeeMicroLink); + } + + /** + * @notice update the list of keepers allowed to perform upkeep + * @param keepers list of addresses allowed to perform upkeep + * @param payees addresses corresponding to keepers who are allowed to + * move payments which have been accrued + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { + require(keepers.length == payees.length, "address lists not the same length"); + require(keepers.length >= 2, "not enough keepers"); + for (uint256 i = 0; i < s_keeperList.length; i++) { + address keeper = s_keeperList[i]; + s_keeperInfo[keeper].active = false; + } + for (uint256 i = 0; i < keepers.length; i++) { + address keeper = keepers[i]; + KeeperInfo storage s_keeper = s_keeperInfo[keeper]; + address oldPayee = s_keeper.payee; + address newPayee = payees[i]; + require(newPayee != address(0), "cannot set payee to the zero address"); + require(oldPayee == ZERO_ADDRESS || oldPayee == newPayee || newPayee == IGNORE_ADDRESS, "cannot change payee"); + require(!s_keeper.active, "cannot add keeper twice"); + s_keeper.active = true; + if (newPayee != IGNORE_ADDRESS) { + s_keeper.payee = newPayee; + } + } + s_keeperList = keepers; + emit KeepersUpdated(keepers, payees); + } + + /** + * @notice update registrar + * @param registrar new registrar + */ + function setRegistrar(address registrar) external onlyOwnerOrRegistrar { + address previous = s_registrar; + require(registrar != previous, "Same registrar"); + s_registrar = registrar; + emit RegistrarChanged(previous, registrar); + } + + // GETTERS + + /** + * @notice read all of the details about an upkeep + */ + function getUpkeep(uint256 id) + external + view + override + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber + ) + { + Upkeep memory reg = s_upkeep[id]; + return ( + reg.target, + reg.executeGas, + s_checkData[id], + reg.balance, + reg.lastKeeper, + reg.admin, + reg.maxValidBlocknumber + ); + } + + /** + * @notice read the total number of upkeep's registered + */ + function getUpkeepCount() external view override returns (uint256) { + return s_upkeepCount; + } + + /** + * @notice read the current list canceled upkeep IDs + */ + function getCanceledUpkeepList() external view override returns (uint256[] memory) { + return s_canceledUpkeepList; + } + + /** + * @notice read the current list of addresses allowed to perform upkeep + */ + function getKeeperList() external view override returns (address[] memory) { + return s_keeperList; + } + + /** + * @notice read the current registrar + */ + function getRegistrar() external view returns (address) { + return s_registrar; + } + + /** + * @notice read the current info about any keeper address + */ + function getKeeperInfo(address query) + external + view + override + returns ( + address payee, + bool active, + uint96 balance + ) + { + KeeperInfo memory keeper = s_keeperInfo[query]; + return (keeper.payee, keeper.active, keeper.balance); + } + + /** + * @notice read the current configuration of the registry + */ + function getConfig() + external + view + override + returns ( + uint32 paymentPremiumPPB, + uint24 blockCountPerTurn, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ) + { + Config memory config = s_config; + return ( + config.paymentPremiumPPB, + config.blockCountPerTurn, + config.checkGasLimit, + config.stalenessSeconds, + config.gasCeilingMultiplier, + s_fallbackGasPrice, + s_fallbackLinkPrice + ); + } + + /** + * @notice getFlatFee gets the flat rate fee charged to customers when performing upkeep, + * in units of of micro LINK + */ + function getFlatFee() external view returns (uint32) { + return s_config.flatFeeMicroLink; + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + */ + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { + return getMaxPaymentForGas(s_upkeep[id].executeGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + */ + function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { + (uint256 gasWei, uint256 linkEth) = getFeedData(); + uint256 adjustedGasWei = adjustGasPrice(gasWei, false); + return calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + } + + // PRIVATE + + /** + * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function getFeedData() private view returns (uint256 gasWei, uint256 linkEth) { + uint32 stalenessSeconds = s_config.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + linkEth = s_fallbackLinkPrice; + } else { + linkEth = uint256(feedValue); + } + return (gasWei, linkEth); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + */ + function calculatePaymentAmount( + uint256 gasLimit, + uint256 gasWei, + uint256 linkEth + ) private view returns (uint96 payment) { + Config memory config = s_config; + uint256 weiForGas = gasWei.mul(gasLimit.add(REGISTRY_GAS_OVERHEAD)); + uint256 premium = PPB_BASE.add(config.paymentPremiumPPB); + uint256 total = weiForGas.mul(1e9).mul(premium).div(linkEth).add(uint256(config.flatFeeMicroLink).mul(1e12)); + require(total <= LINK_TOTAL_SUPPLY, "payment greater than all LINK"); + return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available + */ + function callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= CUSHION and check for underflow + if lt(g, CUSHION) { + revert(0, 0) + } + g := sub(g, CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * keeper and the exact gas required by the Upkeep + */ + function performUpkeepWithParams(PerformParams memory params) + private + nonReentrant + validUpkeep(params.id) + returns (bool success) + { + Upkeep memory upkeep = s_upkeep[params.id]; + prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + uint256 gasUsed = gasleft(); + bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); + success = callWithExactGas(params.gasLimit, upkeep.target, callData); + gasUsed = gasUsed - gasleft(); + + uint96 payment = calculatePaymentAmount(gasUsed, params.adjustedGasWei, params.linkEth); + + uint96 newUpkeepBalance = s_upkeep[params.id].balance.sub(payment); + s_upkeep[params.id].balance = newUpkeepBalance; + s_upkeep[params.id].lastKeeper = params.from; + + uint96 newKeeperBalance = s_keeperInfo[params.from].balance.add(payment); + s_keeperInfo[params.from].balance = newKeeperBalance; + + emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); + return success; + } + + /** + * @dev ensures a upkeep is valid + */ + function validateUpkeep(uint256 id) private view { + require(s_upkeep[id].maxValidBlocknumber > block.number, "invalid upkeep id"); + } + + /** + * @dev ensures all required checks are passed before an upkeep is performed + */ + function prePerformUpkeep( + Upkeep memory upkeep, + address from, + uint256 maxLinkPayment + ) private view { + require(s_keeperInfo[from].active, "only active keepers"); + require(upkeep.balance >= maxLinkPayment, "insufficient funds"); + require(upkeep.lastKeeper != from, "keepers must take turns"); + } + + /** + * @dev adjusts the gas price to min(ceiling, tx.gasprice) or just uses the ceiling if tx.gasprice is disabled + */ + function adjustGasPrice(uint256 gasWei, bool useTxGasPrice) private view returns (uint256 adjustedPrice) { + adjustedPrice = gasWei.mul(s_config.gasCeilingMultiplier); + if (useTxGasPrice && tx.gasprice < adjustedPrice) { + adjustedPrice = tx.gasprice; + } + } + + /** + * @dev generates a PerformParams struct for use in performUpkeepWithParams() + */ + function generatePerformParams( + address from, + uint256 id, + bytes memory performData, + bool useTxGasPrice + ) private view returns (PerformParams memory) { + uint256 gasLimit = s_upkeep[id].executeGas; + (uint256 gasWei, uint256 linkEth) = getFeedData(); + uint256 adjustedGasWei = adjustGasPrice(gasWei, useTxGasPrice); + uint96 maxLinkPayment = calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + + return + PerformParams({ + from: from, + id: id, + performData: performData, + maxLinkPayment: maxLinkPayment, + gasLimit: gasLimit, + adjustedGasWei: adjustedGasWei, + linkEth: linkEth + }); + } + + /** + * @dev extracts a revert reason from a call result payload + */ + function getRevertMsg(bytes memory _payload) private pure returns (string memory) { + if (_payload.length < 68) return "transaction reverted silently"; + assembly { + _payload := add(_payload, 0x04) + } + return abi.decode(_payload, (string)); + } + + // MODIFIERS + + /** + * @dev ensures a upkeep is valid + */ + modifier validUpkeep(uint256 id) { + validateUpkeep(id); + _; + } + + /** + * @dev ensures that burns don't accidentally happen by sending to the zero + * address + */ + modifier validateRecipient(address to) { + require(to != address(0), "cannot send to zero address"); + _; + } + + /** + * @dev Reverts if called by anyone other than the contract owner or registrar. + */ + modifier onlyOwnerOrRegistrar() { + require(msg.sender == owner() || msg.sender == s_registrar, "Only callable by owner or registrar"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/LinkTokenReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/LinkTokenReceiver.sol new file mode 100644 index 0000000..d98cc25 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/LinkTokenReceiver.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +abstract contract LinkTokenReceiver { + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @dev The data payload's first 2 words will be overwritten by the `sender` and `amount` + * values to ensure correctness. Calls oracleRequest. + * @param sender Address of the sender + * @param amount Amount of LINK sent (specified in wei) + * @param data Payload of the transaction + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes memory data + ) public validateFromLINK permittedFunctionsForLINK(data) { + assembly { + // solhint-disable-next-line avoid-low-level-calls + mstore(add(data, 36), sender) // ensure correct sender is passed + // solhint-disable-next-line avoid-low-level-calls + 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 + require(success, "Unable to create request"); + } + + function getChainlinkToken() public view virtual returns (address); + + /** + * @notice Validate the function called on token transfer + */ + function _validateTokenTransferAction(bytes4 funcSelector, bytes memory data) internal virtual; + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier validateFromLINK() { + require(msg.sender == getChainlinkToken(), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `oracleRequest` function selector + * @param data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(data, 32)) + } + _validateTokenTransferAction(funcSelector, data); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/Operator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/Operator.sol new file mode 100644 index 0000000..ee338d2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/Operator.sol @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./AuthorizedReceiver.sol"; +import "./LinkTokenReceiver.sol"; +import "./ConfirmedOwner.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/OperatorInterface.sol"; +import "./interfaces/OwnableInterface.sol"; +import "./interfaces/WithdrawalInterface.sol"; +import "./vendor/Address.sol"; +import "./vendor/SafeMathChainlink.sol"; + +/** + * @title The Chainlink Operator contract + * @notice Node operators can deploy this contract to fulfill requests sent to them + */ +contract Operator is AuthorizedReceiver, ConfirmedOwner, LinkTokenReceiver, OperatorInterface, WithdrawalInterface { + using Address for address; + using SafeMathChainlink for uint256; + + struct Commitment { + bytes31 paramsHash; + uint8 dataVersion; + } + + uint256 public constant getExpiryTime = 5 minutes; + uint256 private constant MAXIMUM_DATA_VERSION = 256; + uint256 private constant MINIMUM_CONSUMER_GAS_LIMIT = 400000; + 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); + // We initialize fields to 1 instead of 0 so that the first invocation + // does not cost more gas. + uint256 private constant ONE_FOR_CONSISTENT_GAS_COST = 1; + // oracleRequest is intended for version 1, enabling single word responses + bytes4 private constant ORACLE_REQUEST_SELECTOR = this.oracleRequest.selector; + // operatorRequest is intended for version 2, enabling multi-word responses + bytes4 private constant OPERATOR_REQUEST_SELECTOR = this.operatorRequest.selector; + + LinkTokenInterface internal immutable linkToken; + mapping(bytes32 => Commitment) private s_commitments; + mapping(address => bool) private s_owned; + // Tokens sent for requests that have not been fulfilled yet + uint256 private s_tokensInEscrow = ONE_FOR_CONSISTENT_GAS_COST; + + 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); + + event OracleResponse(bytes32 indexed requestId); + + event OwnableContractAccepted(address indexed acceptedContract); + + event TargetsUpdatedAuthorizedSenders(address[] targets, address[] senders, address changedBy); + + /** + * @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 + * @param owner The address of the owner + */ + constructor(address link, address owner) ConfirmedOwner(owner) { + linkToken = LinkTokenInterface(link); // external but already deployed and unalterable + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual returns (string memory) { + return "Operator 1.0.0"; + } + + /** + * @notice Creates the Chainlink request. This is a backwards compatible API + * with the Oracle.sol contract, but the behavior changes because + * callbackAddress is assumed to be the same as the request sender. + * @param callbackAddress The consumer of the request + * @param payment The amount of payment given (specified in wei) + * @param specId The Job Specification ID + * @param callbackAddress The address the oracle data will be sent to + * @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 extra request parameters + */ + function oracleRequest( + address sender, + uint256 payment, + bytes32 specId, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external override validateFromLINK { + (bytes32 requestId, uint256 expiration) = _verifyAndProcessOracleRequest( + sender, + payment, + callbackAddress, + callbackFunctionId, + nonce, + dataVersion + ); + emit OracleRequest(specId, sender, requestId, payment, sender, callbackFunctionId, expiration, dataVersion, data); + } + + /** + * @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 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 extra request parameters + */ + function operatorRequest( + address sender, + uint256 payment, + bytes32 specId, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external override validateFromLINK { + (bytes32 requestId, uint256 expiration) = _verifyAndProcessOracleRequest( + sender, + payment, + sender, + callbackFunctionId, + nonce, + dataVersion + ); + emit OracleRequest(specId, sender, requestId, payment, sender, 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 payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + * @param data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) + external + override + validateAuthorizedSender + validateRequestId(requestId) + validateCallbackAddress(callbackAddress) + returns (bool) + { + _verifyOracleRequestAndProcessPayment(requestId, payment, callbackAddress, callbackFunctionId, expiration, 1); + emit OracleResponse(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, ) = callbackAddress.call(abi.encodeWithSelector(callbackFunctionId, requestId, data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Called by the Chainlink node to fulfill requests with multi-word support + * @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 payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + * @param data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest2( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes calldata data + ) + external + override + validateAuthorizedSender + validateRequestId(requestId) + validateCallbackAddress(callbackAddress) + validateMultiWordResponseId(requestId, data) + returns (bool) + { + _verifyOracleRequestAndProcessPayment(requestId, payment, callbackAddress, callbackFunctionId, expiration, 2); + emit OracleResponse(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, ) = callbackAddress.call(abi.encodePacked(callbackFunctionId, data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Transfer the ownership of ownable contracts. This is primarily + * intended for Authorized Forwarders but could possibly be extended to work + * with future contracts. + * @param ownable list of addresses to transfer + * @param newOwner address to transfer ownership to + */ + function transferOwnableContracts(address[] calldata ownable, address newOwner) external onlyOwner { + for (uint256 i = 0; i < ownable.length; i++) { + s_owned[ownable[i]] = false; + OwnableInterface(ownable[i]).transferOwnership(newOwner); + } + } + + /** + * @notice Accept the ownership of an ownable contract. This is primarily + * intended for Authorized Forwarders but could possibly be extended to work + * with future contracts. + * @dev Must be the pending owner on the contract + * @param ownable list of addresses of Ownable contracts to accept + */ + function acceptOwnableContracts(address[] calldata ownable) public validateAuthorizedSenderSetter { + for (uint256 i = 0; i < ownable.length; i++) { + s_owned[ownable[i]] = true; + emit OwnableContractAccepted(ownable[i]); + OwnableInterface(ownable[i]).acceptOwnership(); + } + } + + /** + * @notice Sets the fulfillment permission for + * @param targets The addresses to set permissions on + * @param senders The addresses that are allowed to send updates + */ + function setAuthorizedSendersOn(address[] calldata targets, address[] calldata senders) + public + validateAuthorizedSenderSetter + { + TargetsUpdatedAuthorizedSenders(targets, senders, msg.sender); + + for (uint256 i = 0; i < targets.length; i++) { + AuthorizedReceiverInterface(targets[i]).setAuthorizedSenders(senders); + } + } + + /** + * @notice Accepts ownership of ownable contracts and then immediately sets + * the authorized sender list on each of the newly owned contracts. This is + * primarily intended for Authorized Forwarders but could possibly be + * extended to work with future contracts. + * @param targets The addresses to set permissions on + * @param senders The addresses that are allowed to send updates + */ + function acceptAuthorizedReceivers(address[] calldata targets, address[] calldata senders) + external + validateAuthorizedSenderSetter + { + acceptOwnableContracts(targets); + setAuthorizedSendersOn(targets, senders); + } + + /** + * @notice Allows the node operator to withdraw earned LINK to a given address + * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node + * @param recipient The address to send the LINK token to + * @param amount The amount to send (specified in wei) + */ + function withdraw(address recipient, uint256 amount) + external + override(OracleInterface, WithdrawalInterface) + onlyOwner + validateAvailableFunds(amount) + { + assert(linkToken.transfer(recipient, amount)); + } + + /** + * @notice Displays the amount of LINK that is available for the node operator to withdraw + * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage + * @return The amount of withdrawable LINK on the contract + */ + function withdrawable() external view override(OracleInterface, WithdrawalInterface) returns (uint256) { + return _fundsAvailable(); + } + + /** + * @notice Forward a call to another contract + * @dev Only callable by the owner + * @param to address + * @param data to forward + */ + function ownerForward(address to, bytes calldata data) external onlyOwner validateNotToLINK(to) { + require(to.isContract(), "Must forward to a contract"); + (bool status, ) = to.call(data); + require(status, "Forwarded call failed"); + } + + /** + * @notice Interact with other LinkTokenReceiver contracts by calling transferAndCall + * @param to The address to transfer to. + * @param value The amount to be transferred. + * @param data The extra data to be passed to the receiving contract. + * @return success bool + */ + function ownerTransferAndCall( + address to, + uint256 value, + bytes calldata data + ) external override onlyOwner validateAvailableFunds(value) returns (bool success) { + return linkToken.transferAndCall(to, value, data); + } + + /** + * @notice Distribute funds to multiple addresses using ETH send + * to this payable function. + * @dev Array length must be equal, ETH sent must equal the sum of amounts. + * A malicious receiver could cause the distribution to revert, in which case + * it is expected that the address is removed from the list. + * @param receivers list of addresses + * @param amounts list of amounts + */ + function distributeFunds(address payable[] calldata receivers, uint256[] calldata amounts) external payable { + require(receivers.length > 0 && receivers.length == amounts.length, "Invalid array length(s)"); + uint256 valueRemaining = msg.value; + for (uint256 i = 0; i < receivers.length; i++) { + uint256 sendAmount = amounts[i]; + valueRemaining = valueRemaining.sub(sendAmount); + receivers[i].transfer(sendAmount); + } + require(valueRemaining == 0, "Too much ETH sent"); + } + + /** + * @notice Allows recipient to cancel requests sent to this oracle contract. + * Will transfer the LINK sent for the request back to the recipient 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 callbackFunc The requester's specified callback function selector + * @param expiration The time of the expiration for the request + */ + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunc, + uint256 expiration + ) external override { + bytes31 paramsHash = _buildParamsHash(payment, msg.sender, callbackFunc, expiration); + require(s_commitments[requestId].paramsHash == paramsHash, "Params do not match request ID"); + // solhint-disable-next-line not-rely-on-time + require(expiration <= block.timestamp, "Request is not expired"); + + delete s_commitments[requestId]; + emit CancelOracleRequest(requestId); + + linkToken.transfer(msg.sender, payment); + } + + /** + * @notice Allows requester to cancel requests sent to this oracle contract. + * Will transfer the LINK sent for the request back to the recipient 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 nonce The nonce used to generate the request ID + * @param payment The amount of payment given (specified in wei) + * @param callbackFunc The requester's specified callback function selector + * @param expiration The time of the expiration for the request + */ + function cancelOracleRequestByRequester( + uint256 nonce, + uint256 payment, + bytes4 callbackFunc, + uint256 expiration + ) external { + bytes32 requestId = keccak256(abi.encodePacked(msg.sender, nonce)); + bytes31 paramsHash = _buildParamsHash(payment, msg.sender, callbackFunc, expiration); + require(s_commitments[requestId].paramsHash == paramsHash, "Params do not match request ID"); + // solhint-disable-next-line not-rely-on-time + require(expiration <= block.timestamp, "Request is not expired"); + + delete s_commitments[requestId]; + emit CancelOracleRequest(requestId); + + 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); + } + + /** + * @notice Require that the token transfer action is valid + * @dev OPERATOR_REQUEST_SELECTOR = multiword, ORACLE_REQUEST_SELECTOR = singleword + */ + function _validateTokenTransferAction(bytes4 funcSelector, bytes memory data) internal pure override { + require(data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); + require( + funcSelector == OPERATOR_REQUEST_SELECTOR || funcSelector == ORACLE_REQUEST_SELECTOR, + "Must use whitelisted functions" + ); + } + + /** + * @notice Verify the Oracle Request and record necessary information + * @param sender The sender of the request + * @param payment The amount of payment given (specified in wei) + * @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 + */ + function _verifyAndProcessOracleRequest( + address sender, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion + ) private validateNotToLINK(callbackAddress) returns (bytes32 requestId, uint256 expiration) { + requestId = keccak256(abi.encodePacked(sender, nonce)); + require(s_commitments[requestId].paramsHash == 0, "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + expiration = block.timestamp.add(getExpiryTime); + bytes31 paramsHash = _buildParamsHash(payment, callbackAddress, callbackFunctionId, expiration); + s_commitments[requestId] = Commitment(paramsHash, _safeCastToUint8(dataVersion)); + s_tokensInEscrow = s_tokensInEscrow.add(payment); + return (requestId, expiration); + } + + /** + * @notice Verify the Oracle request and unlock escrowed payment + * @param requestId The fulfillment request ID that must match the requester's + * @param payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + */ + function _verifyOracleRequestAndProcessPayment( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + uint256 dataVersion + ) internal { + bytes31 paramsHash = _buildParamsHash(payment, callbackAddress, callbackFunctionId, expiration); + require(s_commitments[requestId].paramsHash == paramsHash, "Params do not match request ID"); + require(s_commitments[requestId].dataVersion <= _safeCastToUint8(dataVersion), "Data versions must match"); + s_tokensInEscrow = s_tokensInEscrow.sub(payment); + delete s_commitments[requestId]; + } + + /** + * @notice Build the bytes31 hash from the payment, callback and expiration. + * @param payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + * @return hash bytes31 + */ + function _buildParamsHash( + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration + ) internal pure returns (bytes31) { + return bytes31(keccak256(abi.encodePacked(payment, callbackAddress, callbackFunctionId, expiration))); + } + + /** + * @notice Safely cast uint256 to uint8 + * @param number uint256 + * @return uint8 number + */ + function _safeCastToUint8(uint256 number) internal pure returns (uint8) { + require(number < MAXIMUM_DATA_VERSION, "number too big to cast"); + return uint8(number); + } + + /** + * @notice Returns the LINK available in this contract, not locked in escrow + * @return uint256 LINK tokens available + */ + function _fundsAvailable() private view returns (uint256) { + uint256 inEscrow = s_tokensInEscrow.sub(ONE_FOR_CONSISTENT_GAS_COST); + return linkToken.balanceOf(address(this)).sub(inEscrow); + } + + /** + * @notice concrete implementation of AuthorizedReceiver + * @return bool of whether sender is authorized + */ + function _canSetAuthorizedSenders() internal view override returns (bool) { + return isAuthorizedSender(msg.sender) || owner() == msg.sender; + } + + // MODIFIERS + + /** + * @dev Reverts if the first 32 bytes of the bytes array is not equal to requestId + * @param requestId bytes32 + * @param data bytes + */ + modifier validateMultiWordResponseId(bytes32 requestId, bytes calldata data) { + require(data.length >= 32, "Response must be > 32 bytes"); + bytes32 firstDataWord; + assembly { + // extract the first word from data + // functionSelector = 4 + // wordLength = 32 + // dataArgumentOffset = 7 * wordLength + // funcSelector + dataArgumentOffset == 0xe4 + firstDataWord := calldataload(0xe4) + } + require(requestId == firstDataWord, "First word must be requestId"); + _; + } + + /** + * @dev Reverts if amount requested is greater than withdrawable balance + * @param amount The given amount to compare to `s_withdrawableTokens` + */ + modifier validateAvailableFunds(uint256 amount) { + require(_fundsAvailable() >= amount, "Amount requested is greater than withdrawable balance"); + _; + } + + /** + * @dev Reverts if request ID does not exist + * @param requestId The given request ID to check in stored `commitments` + */ + modifier validateRequestId(bytes32 requestId) { + require(s_commitments[requestId].paramsHash != 0, "Must have a valid requestId"); + _; + } + + /** + * @dev Reverts if the callback address is the LINK token + * @param to The callback address + */ + modifier validateNotToLINK(address to) { + require(to != address(linkToken), "Cannot call to LINK"); + _; + } + + /** + * @dev Reverts if the target address is owned by the operator + */ + modifier validateCallbackAddress(address callbackAddress) { + require(!s_owned[callbackAddress], "Cannot call owned contract"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/OperatorFactory.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/OperatorFactory.sol new file mode 100644 index 0000000..b137ab4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/OperatorFactory.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./Operator.sol"; +import "./AuthorizedForwarder.sol"; + +/** + * @title Operator Factory + * @notice Creates Operator contracts for node operators + */ +contract OperatorFactory { + address public immutable getChainlinkToken; + mapping(address => bool) private s_created; + + event OperatorCreated(address indexed operator, address indexed owner, address indexed sender); + event AuthorizedForwarderCreated(address indexed forwarder, address indexed owner, address indexed sender); + + /** + * @param linkAddress address + */ + constructor(address linkAddress) { + getChainlinkToken = linkAddress; + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual returns (string memory) { + return "OperatorFactory 1.0.0"; + } + + /** + * @notice creates a new Operator contract with the msg.sender as owner + */ + function deployNewOperator() external returns (address) { + Operator operator = new Operator(getChainlinkToken, msg.sender); + + s_created[address(operator)] = true; + emit OperatorCreated(address(operator), msg.sender, msg.sender); + + return address(operator); + } + + /** + * @notice creates a new Operator contract with the msg.sender as owner and a + * new Operator Forwarder with the Operator as the owner + */ + function deployNewOperatorAndForwarder() external returns (address, address) { + Operator operator = new Operator(getChainlinkToken, msg.sender); + s_created[address(operator)] = true; + emit OperatorCreated(address(operator), msg.sender, msg.sender); + + bytes memory tmp = new bytes(0); + AuthorizedForwarder forwarder = new AuthorizedForwarder(getChainlinkToken, address(this), address(operator), tmp); + s_created[address(forwarder)] = true; + emit AuthorizedForwarderCreated(address(forwarder), address(this), msg.sender); + + return (address(operator), address(forwarder)); + } + + /** + * @notice creates a new Forwarder contract with the msg.sender as owner + */ + function deployNewForwarder() external returns (address) { + bytes memory tmp = new bytes(0); + AuthorizedForwarder forwarder = new AuthorizedForwarder(getChainlinkToken, msg.sender, address(0), tmp); + + s_created[address(forwarder)] = true; + emit AuthorizedForwarderCreated(address(forwarder), msg.sender, msg.sender); + + return address(forwarder); + } + + /** + * @notice creates a new Forwarder contract with the msg.sender as owner + */ + function deployNewForwarderAndTransferOwnership(address to, bytes calldata message) external returns (address) { + AuthorizedForwarder forwarder = new AuthorizedForwarder(getChainlinkToken, msg.sender, to, message); + + s_created[address(forwarder)] = true; + emit AuthorizedForwarderCreated(address(forwarder), msg.sender, msg.sender); + + return address(forwarder); + } + + /** + * @notice indicates whether this factory deployed an address + */ + function created(address query) external view returns (bool) { + return s_created[query]; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/UpkeepRegistrationRequests.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/UpkeepRegistrationRequests.sol new file mode 100644 index 0000000..9c0fe6e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/UpkeepRegistrationRequests.sol @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperRegistryInterface.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./vendor/SafeMath96.sol"; +import "./ConfirmedOwner.sol"; + +/** + * @notice Contract to accept requests for upkeep registrations + * @dev There are 2 registration workflows in this contract + * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually + * calls `approve` to register upkeep and emit events to inform UI and others interested. + * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on + * keeper registry and then emits approved event to finish the flow automatically without manual intervention. + * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not. + * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations. + */ +contract UpkeepRegistrationRequests is TypeAndVersionInterface, ConfirmedOwner { + using SafeMath96 for uint96; + + bytes4 private constant REGISTER_REQUEST_SELECTOR = this.register.selector; + + uint256 private s_minLINKJuels; + mapping(bytes32 => PendingRequest) private s_pendingRequests; + + LinkTokenInterface public immutable LINK; + + /** + * @notice versions: + * - UpkeepRegistration 1.0.0: initial release + */ + string public constant override typeAndVersion = "UpkeepRegistrationRequests 1.0.0"; + + struct AutoApprovedConfig { + bool enabled; + uint16 allowedPerWindow; + uint32 windowSizeInBlocks; + uint64 windowStart; + uint16 approvedInCurrentWindow; + } + + struct PendingRequest { + address admin; + uint96 balance; + } + + AutoApprovedConfig private s_config; + KeeperRegistryBaseInterface private s_keeperRegistry; + + event RegistrationRequested( + bytes32 indexed hash, + string name, + bytes encryptedEmail, + address indexed upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes checkData, + uint96 amount, + uint8 indexed source + ); + + event RegistrationApproved(bytes32 indexed hash, string displayName, uint256 indexed upkeepId); + + event RegistrationRejected(bytes32 indexed hash); + + event ConfigChanged( + bool enabled, + uint32 windowSizeInBlocks, + uint16 allowedPerWindow, + address keeperRegistry, + uint256 minLINKJuels + ); + + constructor(address LINKAddress, uint256 minimumLINKJuels) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(LINKAddress); + s_minLINKJuels = minimumLINKJuels; + } + + //EXTERNAL + + /** + * @notice register can only be called through transferAndCall on LINK contract + * @param name string of the upkeep to be registered + * @param encryptedEmail email address of upkeep contact + * @param upkeepContract address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when performing upkeep + * @param adminAddress address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + * @param amount quantity of LINK upkeep is funded with (specified in Juels) + * @param source application sending this request + */ + function register( + string memory name, + bytes calldata encryptedEmail, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + uint8 source + ) external onlyLINK { + require(adminAddress != address(0), "invalid admin address"); + bytes32 hash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); + + emit RegistrationRequested( + hash, + name, + encryptedEmail, + upkeepContract, + gasLimit, + adminAddress, + checkData, + amount, + source + ); + + AutoApprovedConfig memory config = s_config; + if (config.enabled && _underApprovalLimit(config)) { + _incrementApprovedCount(config); + + _approve(name, upkeepContract, gasLimit, adminAddress, checkData, amount, hash); + } else { + uint96 newBalance = s_pendingRequests[hash].balance.add(amount); + s_pendingRequests[hash] = PendingRequest({admin: adminAddress, balance: newBalance}); + } + } + + /** + * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event + */ + function approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + bytes32 hash + ) external onlyOwner { + PendingRequest memory request = s_pendingRequests[hash]; + require(request.admin != address(0), "request not found"); + bytes32 expectedHash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); + require(hash == expectedHash, "hash and payload do not match"); + delete s_pendingRequests[hash]; + _approve(name, upkeepContract, gasLimit, adminAddress, checkData, request.balance, hash); + } + + /** + * @notice cancel will remove a registration request and return the refunds to the msg.sender + * @param hash the request hash + */ + function cancel(bytes32 hash) external { + PendingRequest memory request = s_pendingRequests[hash]; + require(msg.sender == request.admin || msg.sender == owner(), "only admin / owner can cancel"); + require(request.admin != address(0), "request not found"); + delete s_pendingRequests[hash]; + require(LINK.transfer(msg.sender, request.balance), "LINK token transfer failed"); + emit RegistrationRejected(hash); + } + + /** + * @notice owner calls this function to set if registration requests should be sent directly to the Keeper Registry + * @param enabled setting for auto-approve registrations + * @param windowSizeInBlocks window size defined in number of blocks + * @param allowedPerWindow number of registrations that can be auto approved in above window + * @param keeperRegistry new keeper registry address + */ + function setRegistrationConfig( + bool enabled, + uint32 windowSizeInBlocks, + uint16 allowedPerWindow, + address keeperRegistry, + uint256 minLINKJuels + ) external onlyOwner { + s_config = AutoApprovedConfig({ + enabled: enabled, + allowedPerWindow: allowedPerWindow, + windowSizeInBlocks: windowSizeInBlocks, + windowStart: 0, + approvedInCurrentWindow: 0 + }); + s_minLINKJuels = minLINKJuels; + s_keeperRegistry = KeeperRegistryBaseInterface(keeperRegistry); + + emit ConfigChanged(enabled, windowSizeInBlocks, allowedPerWindow, keeperRegistry, minLINKJuels); + } + + /** + * @notice read the current registration configuration + */ + function getRegistrationConfig() + external + view + returns ( + bool enabled, + uint32 windowSizeInBlocks, + uint16 allowedPerWindow, + address keeperRegistry, + uint256 minLINKJuels, + uint64 windowStart, + uint16 approvedInCurrentWindow + ) + { + AutoApprovedConfig memory config = s_config; + return ( + config.enabled, + config.windowSizeInBlocks, + config.allowedPerWindow, + address(s_keeperRegistry), + s_minLINKJuels, + config.windowStart, + config.approvedInCurrentWindow + ); + } + + /** + * @notice gets the admin address and the current balance of a registration request + */ + function getPendingRequest(bytes32 hash) external view returns (address, uint96) { + PendingRequest memory request = s_pendingRequests[hash]; + return (request.admin, request.balance); + } + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @param amount Amount of LINK sent (specified in Juels) + * @param data Payload of the transaction + */ + function onTokenTransfer( + address, /* sender */ + uint256 amount, + bytes calldata data + ) external onlyLINK permittedFunctionsForLINK(data) isActualAmount(amount, data) { + require(amount >= s_minLINKJuels, "Insufficient payment"); + (bool success, ) = address(this).delegatecall(data); + // calls register + require(success, "Unable to create request"); + } + + //PRIVATE + + /** + * @dev reset auto approve window if passed end of current window + */ + function _resetWindowIfRequired(AutoApprovedConfig memory config) private { + uint64 blocksPassed = uint64(block.number - config.windowStart); + if (blocksPassed >= config.windowSizeInBlocks) { + config.windowStart = uint64(block.number); + config.approvedInCurrentWindow = 0; + s_config = config; + } + } + + /** + * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event + */ + function _approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + bytes32 hash + ) private { + KeeperRegistryBaseInterface keeperRegistry = s_keeperRegistry; + + // register upkeep + uint256 upkeepId = keeperRegistry.registerUpkeep(upkeepContract, gasLimit, adminAddress, checkData); + // fund upkeep + bool success = LINK.transferAndCall(address(keeperRegistry), amount, abi.encode(upkeepId)); + require(success, "failed to fund upkeep"); + + emit RegistrationApproved(hash, name, upkeepId); + } + + /** + * @dev determine approval limits and check if in range + */ + function _underApprovalLimit(AutoApprovedConfig memory config) private returns (bool) { + _resetWindowIfRequired(config); + if (config.approvedInCurrentWindow < config.allowedPerWindow) { + return true; + } + return false; + } + + /** + * @dev record new latest approved count + */ + function _incrementApprovedCount(AutoApprovedConfig memory config) private { + config.approvedInCurrentWindow++; + s_config = config; + } + + //MODIFIERS + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + require(msg.sender == address(LINK), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `register` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory _data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(_data, 32)) + } + require(funcSelector == REGISTER_REQUEST_SELECTOR, "Must use whitelisted functions"); + _; + } + + /** + * @dev Reverts if the actual amount passed does not match the expected amount + * @param expected amount that should match the actual amount + * @param data bytes + */ + modifier isActualAmount(uint256 expected, bytes memory data) { + uint256 actual; + assembly { + actual := mload(add(data, 228)) + } + require(expected == actual, "Amount mismatch"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/VRFConsumerBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/VRFConsumerBase.sol new file mode 100644 index 0000000..9956c2a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/VRFConsumerBase.sol @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./vendor/SafeMathChainlink.sol"; + +import "./interfaces/LinkTokenInterface.sol"; + +import "./VRFRequestIDBase.sol"; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constructor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator, _link) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash), and have told you the minimum LINK + * @dev price for VRF service. Make sure your contract has sufficient LINK, and + * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you + * @dev want to generate randomness from. + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomness method. + * + * @dev The randomness argument to fulfillRandomness is the actual random value + * @dev generated from your seed. + * + * @dev The requestId argument is generated from the keyHash and the seed by + * @dev makeRequestId(keyHash, seed). If your contract could have concurrent + * @dev requests open, you can use the requestId to track which seed is + * @dev associated with which randomness. See VRFRequestIDBase.sol for more + * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously.) + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. (Which is critical to making unpredictable randomness! See the + * @dev next section.) + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the ultimate input to the VRF is mixed with the block hash of the + * @dev block in which the request is made, user-provided seeds have no impact + * @dev on its economic security properties. They are only included for API + * @dev compatability with previous versions of this contract. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. + */ +abstract contract VRFConsumerBase is VRFRequestIDBase { + using SafeMathChainlink for uint256; + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBase expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomness the VRF output + */ + function fulfillRandomness(bytes32 requestId, uint256 randomness) internal virtual; + + /** + * @dev In order to keep backwards compatibility we have kept the user + * seed field around. We remove the use of it because given that the blockhash + * enters later, it overrides whatever randomness the used seed provides. + * Given that it adds no security, and can easily lead to misunderstandings, + * we have removed it from usage and can now provide a simpler API. + */ + uint256 private constant USER_SEED_PLACEHOLDER = 0; + + /** + * @notice requestRandomness initiates a request for VRF output given _seed + * + * @dev The fulfillRandomness method receives the output, once it's provided + * @dev by the Oracle, and verified by the vrfCoordinator. + * + * @dev The _keyHash must already be registered with the VRFCoordinator, and + * @dev the _fee must exceed the fee specified during registration of the + * @dev _keyHash. + * + * @dev The _seed parameter is vestigial, and is kept only for API + * @dev compatibility with older versions. It can't *hurt* to mix in some of + * @dev your own randomness, here, but it's not necessary because the VRF + * @dev oracle will mix the hash of the block containing your request into the + * @dev VRF seed it ultimately uses. + * + * @param _keyHash ID of public key against which randomness is generated + * @param _fee The amount of LINK to send with the request + * + * @return requestId unique ID for this request + * + * @dev The returned requestId can be used to distinguish responses to + * @dev concurrent requests. It is passed as the first argument to + * @dev fulfillRandomness. + */ + function requestRandomness(bytes32 _keyHash, uint256 _fee) internal returns (bytes32 requestId) { + LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); + // This is the seed passed to VRFCoordinator. The oracle will mix this with + // the hash of the block containing this request to obtain the seed/input + // which is finally passed to the VRF cryptographic machinery. + uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); + // nonces[_keyHash] must stay in sync with + // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above + // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). + // This provides protection against the user repeating their input seed, + // which would result in a predictable/duplicate output, if multiple such + // requests appeared in the same block. + nonces[_keyHash] = nonces[_keyHash].add(1); + return makeRequestId(_keyHash, vRFSeed); + } + + LinkTokenInterface internal immutable LINK; + address private immutable vrfCoordinator; + + // Nonces for each VRF key from which randomness has been requested. + // + // Must stay in sync with VRFCoordinator[_keyHash][this] + mapping(bytes32 => uint256) /* keyHash */ /* nonce */ + private nonces; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + * @param _link address of LINK token contract + * + * @dev https://docs.chain.link/docs/link-token-contracts + */ + constructor(address _vrfCoordinator, address _link) { + vrfCoordinator = _vrfCoordinator; + LINK = LinkTokenInterface(_link); + } + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external { + require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); + fulfillRandomness(requestId, randomness); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/VRFRequestIDBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/VRFRequestIDBase.sol new file mode 100644 index 0000000..87bb6fe --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/VRFRequestIDBase.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract VRFRequestIDBase { + /** + * @notice returns the seed which is actually input to the VRF coordinator + * + * @dev To prevent repetition of VRF output due to repetition of the + * @dev user-supplied seed, that seed is combined in a hash with the + * @dev user-specific nonce, and the address of the consuming contract. The + * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in + * @dev the final seed, but the nonce does protect against repetition in + * @dev requests which are included in a single block. + * + * @param _userSeed VRF seed input provided by user + * @param _requester Address of the requesting contract + * @param _nonce User-specific nonce at the time of the request + */ + function makeVRFInputSeed( + bytes32 _keyHash, + uint256 _userSeed, + address _requester, + uint256 _nonce + ) internal pure returns (uint256) { + return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); + } + + /** + * @notice Returns the id for this request + * @param _keyHash The serviceAgreement ID to be used for this request + * @param _vRFInputSeed The seed to be passed directly to the VRF + * @return The id for this request + * + * @dev Note that _vRFInputSeed is not the seed passed by the consuming + * @dev contract, but the one generated by makeVRFInputSeed + */ + function makeRequestId(bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AggregatorProxy.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AggregatorProxy.sol new file mode 100644 index 0000000..9f68faa --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AggregatorProxy.sol @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../ConfirmedOwner.sol"; +import "../interfaces/AggregatorProxyInterface.sol"; + +/** + * @title A trusted proxy for updating where current answers are read from + * @notice This contract provides a consistent address for the + * CurrentAnswerInterface but delegates where it reads from to the owner, who is + * trusted to update it. + */ +contract AggregatorProxy is AggregatorProxyInterface, ConfirmedOwner { + struct Phase { + uint16 id; + AggregatorProxyInterface aggregator; + } + AggregatorProxyInterface private s_proposedAggregator; + mapping(uint16 => AggregatorProxyInterface) private s_phaseAggregators; + Phase private s_currentPhase; + + uint256 private constant PHASE_OFFSET = 64; + uint256 private constant PHASE_SIZE = 16; + uint256 private constant MAX_ID = 2**(PHASE_OFFSET + PHASE_SIZE) - 1; + + event AggregatorProposed(address indexed current, address indexed proposed); + event AggregatorConfirmed(address indexed previous, address indexed latest); + + constructor(address aggregatorAddress) ConfirmedOwner(msg.sender) { + setAggregator(aggregatorAddress); + } + + /** + * @notice Reads the current answer from aggregator delegated to. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestAnswer() public view virtual override returns (int256 answer) { + return s_currentPhase.aggregator.latestAnswer(); + } + + /** + * @notice Reads the last updated height from aggregator delegated to. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestTimestamp() public view virtual override returns (uint256 updatedAt) { + return s_currentPhase.aggregator.latestTimestamp(); + } + + /** + * @notice get past rounds answers + * @param roundId the answer number to retrieve the answer for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getAnswer(uint256 roundId) public view virtual override returns (int256 answer) { + if (roundId > MAX_ID) return 0; + + (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(roundId); + AggregatorProxyInterface aggregator = s_phaseAggregators[phaseId]; + if (address(aggregator) == address(0)) return 0; + + return aggregator.getAnswer(aggregatorRoundId); + } + + /** + * @notice get block timestamp when an answer was last updated + * @param roundId the answer number to retrieve the updated timestamp for + * + * @dev #[deprecated] Use getRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended getRoundData + * instead which includes better verification information. + */ + function getTimestamp(uint256 roundId) public view virtual override returns (uint256 updatedAt) { + if (roundId > MAX_ID) return 0; + + (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(roundId); + AggregatorProxyInterface aggregator = s_phaseAggregators[phaseId]; + if (address(aggregator) == address(0)) return 0; + + return aggregator.getTimestamp(aggregatorRoundId); + } + + /** + * @notice get the latest completed round where the answer was updated. This + * ID includes the proxy's phase, to make sure round IDs increase even when + * switching to a newly deployed aggregator. + * + * @dev #[deprecated] Use latestRoundData instead. This does not error if no + * answer has been reached, it will simply return 0. Either wait to point to + * an already answered Aggregator or use the recommended latestRoundData + * instead which includes better verification information. + */ + function latestRound() public view virtual override returns (uint256 roundId) { + Phase memory phase = s_currentPhase; // cache storage reads + return addPhase(phase.id, uint64(phase.aggregator.latestRound())); + } + + /** + * @notice get data about a round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * Note that different underlying implementations of AggregatorV3Interface + * have slightly different semantics for some of the return values. Consumers + * should determine what implementations they expect to receive + * data from and validate that they can properly handle return data from all + * of them. + * @param roundId the requested round ID as presented through the proxy, this + * is made up of the aggregator's round ID with the phase ID encoded in the + * two highest order bytes + * @return id is the round ID from the aggregator for which the data was + * retrieved combined with an phase to ensure that round IDs get larger as + * time moves forward. + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @dev Note that answer and updatedAt may change between queries. + */ + function getRoundData(uint80 roundId) + public + view + virtual + override + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(roundId); + + (id, answer, startedAt, updatedAt, answeredInRound) = s_phaseAggregators[phaseId].getRoundData(aggregatorRoundId); + + return addPhaseIds(id, answer, startedAt, updatedAt, answeredInRound, phaseId); + } + + /** + * @notice get data about the latest round. Consumers are encouraged to check + * that they're receiving fresh data by inspecting the updatedAt and + * answeredInRound return values. + * Note that different underlying implementations of AggregatorV3Interface + * have slightly different semantics for some of the return values. Consumers + * should determine what implementations they expect to receive + * data from and validate that they can properly handle return data from all + * of them. + * @return id is the round ID from the aggregator for which the data was + * retrieved combined with an phase to ensure that round IDs get larger as + * time moves forward. + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @dev Note that answer and updatedAt may change between queries. + */ + function latestRoundData() + public + view + virtual + override + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + Phase memory current = s_currentPhase; // cache storage reads + + (id, answer, startedAt, updatedAt, answeredInRound) = current.aggregator.latestRoundData(); + + return addPhaseIds(id, answer, startedAt, updatedAt, answeredInRound, current.id); + } + + /** + * @notice Used if an aggregator contract has been proposed. + * @param roundId the round ID to retrieve the round data for + * @return id is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + */ + function proposedGetRoundData(uint80 roundId) + external + view + virtual + override + hasProposal + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return s_proposedAggregator.getRoundData(roundId); + } + + /** + * @notice Used if an aggregator contract has been proposed. + * @return id is the round ID for which data was retrieved + * @return answer is the answer for the given round + * @return startedAt is the timestamp when the round was started. + * (Only some AggregatorV3Interface implementations return meaningful values) + * @return updatedAt is the timestamp when the round last was updated (i.e. + * answer was last computed) + * @return answeredInRound is the round ID of the round in which the answer + * was computed. + */ + function proposedLatestRoundData() + external + view + virtual + override + hasProposal + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return s_proposedAggregator.latestRoundData(); + } + + /** + * @notice returns the current phase's aggregator address. + */ + function aggregator() external view override returns (address) { + return address(s_currentPhase.aggregator); + } + + /** + * @notice returns the current phase's ID. + */ + function phaseId() external view override returns (uint16) { + return s_currentPhase.id; + } + + /** + * @notice represents the number of decimals the aggregator responses represent. + */ + function decimals() external view override returns (uint8) { + return s_currentPhase.aggregator.decimals(); + } + + /** + * @notice the version number representing the type of aggregator the proxy + * points to. + */ + function version() external view override returns (uint256) { + return s_currentPhase.aggregator.version(); + } + + /** + * @notice returns the description of the aggregator the proxy points to. + */ + function description() external view override returns (string memory) { + return s_currentPhase.aggregator.description(); + } + + /** + * @notice returns the current proposed aggregator + */ + function proposedAggregator() external view override returns (address) { + return address(s_proposedAggregator); + } + + /** + * @notice return a phase aggregator using the phaseId + * + * @param phaseId uint16 + */ + function phaseAggregators(uint16 phaseId) external view override returns (address) { + return address(s_phaseAggregators[phaseId]); + } + + /** + * @notice Allows the owner to propose a new address for the aggregator + * @param aggregatorAddress The new address for the aggregator contract + */ + function proposeAggregator(address aggregatorAddress) external onlyOwner { + s_proposedAggregator = AggregatorProxyInterface(aggregatorAddress); + emit AggregatorProposed(address(s_currentPhase.aggregator), aggregatorAddress); + } + + /** + * @notice Allows the owner to confirm and change the address + * to the proposed aggregator + * @dev Reverts if the given address doesn't match what was previously + * proposed + * @param aggregatorAddress The new address for the aggregator contract + */ + function confirmAggregator(address aggregatorAddress) external onlyOwner { + require(aggregatorAddress == address(s_proposedAggregator), "Invalid proposed aggregator"); + address previousAggregator = address(s_currentPhase.aggregator); + delete s_proposedAggregator; + setAggregator(aggregatorAddress); + emit AggregatorConfirmed(previousAggregator, aggregatorAddress); + } + + /* + * Internal + */ + + function setAggregator(address aggregatorAddress) internal { + uint16 id = s_currentPhase.id + 1; + s_currentPhase = Phase(id, AggregatorProxyInterface(aggregatorAddress)); + s_phaseAggregators[id] = AggregatorProxyInterface(aggregatorAddress); + } + + function addPhase(uint16 phase, uint64 originalId) internal pure returns (uint80) { + return uint80((uint256(phase) << PHASE_OFFSET) | originalId); + } + + function parseIds(uint256 roundId) internal pure returns (uint16, uint64) { + uint16 phaseId = uint16(roundId >> PHASE_OFFSET); + uint64 aggregatorRoundId = uint64(roundId); + + return (phaseId, aggregatorRoundId); + } + + function addPhaseIds( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound, + uint16 phaseId + ) + internal + pure + returns ( + uint80, + int256, + uint256, + uint256, + uint80 + ) + { + return ( + addPhase(phaseId, uint64(roundId)), + answer, + startedAt, + updatedAt, + addPhase(phaseId, uint64(answeredInRound)) + ); + } + + /* + * Modifiers + */ + + modifier hasProposal() { + require(address(s_proposedAggregator) != address(0), "No proposed aggregator present"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AuthorizedForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AuthorizedForwarder.sol new file mode 100644 index 0000000..a78500c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AuthorizedForwarder.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/OperatorInterface.sol"; +import "../ConfirmedOwnerWithProposal.sol"; +import "../AuthorizedReceiver.sol"; +import "../vendor/Address.sol"; + +contract AuthorizedForwarder is ConfirmedOwnerWithProposal, AuthorizedReceiver { + using Address for address; + + address public immutable getChainlinkToken; + + event OwnershipTransferRequestedWithMessage(address indexed from, address indexed to, bytes message); + + constructor( + address link, + address owner, + address recipient, + bytes memory message + ) ConfirmedOwnerWithProposal(owner, recipient) { + getChainlinkToken = link; + if (recipient != address(0)) { + emit OwnershipTransferRequestedWithMessage(owner, recipient, message); + } + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual returns (string memory) { + return "AuthorizedForwarder 1.0.0"; + } + + /** + * @notice Forward a call to another contract + * @dev Only callable by an authorized sender + * @param to address + * @param data to forward + */ + function forward(address to, bytes calldata data) external validateAuthorizedSender { + require(to != getChainlinkToken, "Cannot forward to Link token"); + _forward(to, data); + } + + /** + * @notice Forward a call to another contract + * @dev Only callable by the owner + * @param to address + * @param data to forward + */ + function ownerForward(address to, bytes calldata data) external onlyOwner { + _forward(to, data); + } + + /** + * @notice Transfer ownership with instructions for recipient + * @param to address proposed recipient of ownership + * @param message instructions for recipient upon accepting ownership + */ + function transferOwnershipWithMessage(address to, bytes calldata message) external { + transferOwnership(to); + emit OwnershipTransferRequestedWithMessage(msg.sender, to, message); + } + + /** + * @notice concrete implementation of AuthorizedReceiver + * @return bool of whether sender is authorized + */ + function _canSetAuthorizedSenders() internal view override returns (bool) { + return owner() == msg.sender; + } + + /** + * @notice common forwarding functionality and validation + */ + function _forward(address to, bytes calldata data) private { + require(to.isContract(), "Must forward to a contract"); + (bool success, bytes memory result) = to.call(data); + if (!success) { + if (result.length == 0) revert("Forwarded call reverted without reason"); + assembly { + revert(add(32, result), mload(result)) + } + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AuthorizedReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AuthorizedReceiver.sol new file mode 100644 index 0000000..6ec81c9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/AuthorizedReceiver.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/AuthorizedReceiverInterface.sol"; + +abstract contract AuthorizedReceiver is + AuthorizedReceiverInterface +{ + + mapping(address => bool) private s_authorizedSenders; + address[] private s_authorizedSenderList; + + event AuthorizedSendersChanged( + address[] senders, + address changedBy + ); + + /** + * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. + * @param senders The addresses of the authorized Chainlink node + */ + function setAuthorizedSenders( + address[] calldata senders + ) + external + override + validateAuthorizedSenderSetter() + { + require(senders.length > 0, "Must have at least 1 authorized sender"); + // Set previous authorized senders to false + uint256 authorizedSendersLength = s_authorizedSenderList.length; + for (uint256 i = 0; i < authorizedSendersLength; i++) { + s_authorizedSenders[s_authorizedSenderList[i]] = false; + } + // Set new to true + for (uint256 i = 0; i < senders.length; i++) { + s_authorizedSenders[senders[i]] = true; + } + // Replace list + s_authorizedSenderList = senders; + emit AuthorizedSendersChanged(senders, msg.sender); + } + + /** + * @notice Retrieve a list of authorized senders + * @return array of addresses + */ + function getAuthorizedSenders() + external + view + override + returns ( + address[] memory + ) + { + return s_authorizedSenderList; + } + + /** + * @notice Use this to check if a node is authorized for fulfilling requests + * @param sender The address of the Chainlink node + * @return The authorization status of the node + */ + function isAuthorizedSender( + address sender + ) + public + view + override + returns (bool) + { + return s_authorizedSenders[sender]; + } + + /** + * @notice customizable guard of who can update the authorized sender list + * @return bool whether sender can update authorized sender list + */ + function _canSetAuthorizedSenders() + internal + virtual + returns (bool); + + /** + * @notice validates the sender is an authorized sender + */ + function _validateIsAuthorizedSender() + internal + view + { + require(isAuthorizedSender(msg.sender), "Not authorized sender"); + } + + /** + * @notice prevents non-authorized addresses from calling this method + */ + modifier validateAuthorizedSender() + { + _validateIsAuthorizedSender(); + _; + } + + /** + * @notice prevents non-authorized addresses from calling this method + */ + modifier validateAuthorizedSenderSetter() + { + require(_canSetAuthorizedSenders(), "Cannot set authorized senders"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/CompoundPriceFlaggingValidator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/CompoundPriceFlaggingValidator.sol new file mode 100644 index 0000000..37a15fc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/CompoundPriceFlaggingValidator.sol @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../ConfirmedOwner.sol"; +import "../vendor/SafeMathChainlink.sol"; +import "../interfaces/FlagsInterface.sol"; +import "../interfaces/AggregatorV3Interface.sol"; +import "../interfaces/UniswapAnchoredView.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; + +/** + * @notice This validator compares the price of Chainlink aggregators against + * their equivalent Compound Open Oracle feeds. For each aggregator, a Compound + * feed is configured with its symbol, number of decimals, and deviation threshold. + * An aggregator address is flagged when its corresponding Compound feed price deviates + * by more than the configured threshold from the aggregator price. + */ +contract CompoundPriceFlaggingValidator is ConfirmedOwner, KeeperCompatibleInterface { + using SafeMathChainlink for uint256; + + struct CompoundFeedDetails { + // Used to call the Compound Open Oracle + string symbol; + // Used to convert price to match aggregator decimals + uint8 decimals; + // The numerator used to determine the threshold percentage + // as parts per billion. + // 1,000,000,000 = 100% + // 500,000,000 = 50% + // 100,000,000 = 10% + // 50,000,000 = 5% + // 10,000,000 = 1% + // 2,000,000 = 0.2% + // etc + uint32 deviationThresholdNumerator; + } + + uint256 private constant BILLION = 1_000_000_000; + + FlagsInterface private s_flags; + UniswapAnchoredView private s_compOpenOracle; + mapping(address => CompoundFeedDetails) private s_feedDetails; + + event CompoundOpenOracleAddressUpdated(address indexed from, address indexed to); + event FlagsAddressUpdated(address indexed from, address indexed to); + event FeedDetailsSet(address indexed aggregator, string symbol, uint8 decimals, uint32 deviationThresholdNumerator); + + /** + * @notice Create a new CompoundPriceFlaggingValidator + * @dev Use this contract to compare Chainlink aggregator prices + * against the Compound Open Oracle prices + * @param flagsAddress Address of the flag contract + * @param compoundOracleAddress Address of the Compound Open Oracle UniswapAnchoredView contract + */ + constructor(address flagsAddress, address compoundOracleAddress) ConfirmedOwner(msg.sender) { + setFlagsAddress(flagsAddress); + setCompoundOpenOracleAddress(compoundOracleAddress); + } + + /** + * @notice Set the address of the Compound Open Oracle UniswapAnchoredView contract + * @param oracleAddress Compound Open Oracle UniswapAnchoredView address + */ + function setCompoundOpenOracleAddress(address oracleAddress) public onlyOwner { + address previous = address(s_compOpenOracle); + if (previous != oracleAddress) { + s_compOpenOracle = UniswapAnchoredView(oracleAddress); + emit CompoundOpenOracleAddressUpdated(previous, oracleAddress); + } + } + + /** + * @notice Updates the flagging contract address for raising flags + * @param flagsAddress sets the address of the flags contract + */ + function setFlagsAddress(address flagsAddress) public onlyOwner { + address previous = address(s_flags); + if (previous != flagsAddress) { + s_flags = FlagsInterface(flagsAddress); + emit FlagsAddressUpdated(previous, flagsAddress); + } + } + + /** + * @notice Set the threshold details for comparing a Chainlink aggregator + * to a Compound Open Oracle feed. + * @param aggregator The Chainlink aggregator address + * @param compoundSymbol The symbol used by Compound for this feed + * @param compoundDecimals The number of decimals in the Compound feed + * @param compoundDeviationThresholdNumerator The threshold numerator use to determine + * the percentage with which the difference in prices must reside within. Parts per billion. + * For example: + * If prices are valid within a 5% threshold, assuming x is the compoundDeviationThresholdNumerator: + * x / 1,000,000,000 = 0.05 + * x = 50,000,000 + */ + function setFeedDetails( + address aggregator, + string calldata compoundSymbol, + uint8 compoundDecimals, + uint32 compoundDeviationThresholdNumerator + ) public onlyOwner { + require( + compoundDeviationThresholdNumerator > 0 && compoundDeviationThresholdNumerator <= BILLION, + "Invalid threshold numerator" + ); + require(_compoundPriceOf(compoundSymbol) != 0, "Invalid Compound price"); + string memory currentSymbol = s_feedDetails[aggregator].symbol; + // If symbol is not set, use the new one + if (bytes(currentSymbol).length == 0) { + s_feedDetails[aggregator] = CompoundFeedDetails({ + symbol: compoundSymbol, + decimals: compoundDecimals, + deviationThresholdNumerator: compoundDeviationThresholdNumerator + }); + emit FeedDetailsSet(aggregator, compoundSymbol, compoundDecimals, compoundDeviationThresholdNumerator); + } + // If the symbol is already set, don't change it + else { + s_feedDetails[aggregator] = CompoundFeedDetails({ + symbol: currentSymbol, + decimals: compoundDecimals, + deviationThresholdNumerator: compoundDeviationThresholdNumerator + }); + emit FeedDetailsSet(aggregator, currentSymbol, compoundDecimals, compoundDeviationThresholdNumerator); + } + } + + /** + * @notice Check the price deviation of an array of aggregators + * @dev If any of the aggregators provided have an equivalent Compound Oracle feed + * that with a price outside of the configured deviation, this function will return them. + * @param aggregators address[] memory + * @return address[] invalid feeds + */ + function check(address[] memory aggregators) public view returns (address[] memory) { + address[] memory invalidAggregators = new address[](aggregators.length); + uint256 invalidCount = 0; + for (uint256 i = 0; i < aggregators.length; i++) { + address aggregator = aggregators[i]; + if (_isInvalid(aggregator)) { + invalidAggregators[invalidCount] = aggregator; + invalidCount++; + } + } + + if (aggregators.length != invalidCount) { + assembly { + mstore(invalidAggregators, invalidCount) + } + } + return invalidAggregators; + } + + /** + * @notice Check and raise flags for any aggregator that has an equivalent Compound + * Open Oracle feed with a price deviation exceeding the configured setting. + * @dev This contract must have write permissions on the Flags contract + * @param aggregators address[] memory + * @return address[] memory invalid aggregators + */ + function update(address[] memory aggregators) public returns (address[] memory) { + address[] memory invalidAggregators = check(aggregators); + s_flags.raiseFlags(invalidAggregators); + return invalidAggregators; + } + + /** + * @notice Check the price deviation of an array of aggregators + * @dev If any of the aggregators provided have an equivalent Compound Oracle feed + * that with a price outside of the configured deviation, this function will return them. + * @param data bytes encoded address array + * @return needsUpkeep bool indicating whether upkeep needs to be performed + * @return invalid aggregators - bytes encoded address array of invalid aggregator addresses + */ + function checkUpkeep(bytes calldata data) external view override returns (bool, bytes memory) { + address[] memory invalidAggregators = check(abi.decode(data, (address[]))); + bool needsUpkeep = (invalidAggregators.length > 0); + return (needsUpkeep, abi.encode(invalidAggregators)); + } + + /** + * @notice Check and raise flags for any aggregator that has an equivalent Compound + * Open Oracle feed with a price deviation exceeding the configured setting. + * @dev This contract must have write permissions on the Flags contract + * @param data bytes encoded address array + */ + function performUpkeep(bytes calldata data) external override { + update(abi.decode(data, (address[]))); + } + + /** + * @notice Get the threshold of an aggregator + * @param aggregator address + * @return string Compound Oracle Symbol + * @return uint8 Compound Oracle Decimals + * @return uint32 Deviation Threshold Numerator + */ + function getFeedDetails(address aggregator) + public + view + returns ( + string memory, + uint8, + uint32 + ) + { + CompoundFeedDetails memory compDetails = s_feedDetails[aggregator]; + return (compDetails.symbol, compDetails.decimals, compDetails.deviationThresholdNumerator); + } + + /** + * @notice Get the flags address + * @return address + */ + function flags() external view returns (address) { + return address(s_flags); + } + + /** + * @notice Get the Compound Open Oracle address + * @return address + */ + function compoundOpenOracle() external view returns (address) { + return address(s_compOpenOracle); + } + + /** + * @notice Return the Compound oracle price of an asset using its symbol + * @param symbol string + * @return price uint256 + */ + function _compoundPriceOf(string memory symbol) private view returns (uint256) { + return s_compOpenOracle.price(symbol); + } + + // VALIDATION FUNCTIONS + + /** + * @notice Check if an aggregator has an equivalent Compound Oracle feed + * that's price is deviated more than the threshold. + * @param aggregator address of the Chainlink aggregator + * @return invalid bool. True if the deviation exceeds threshold. + */ + function _isInvalid(address aggregator) private view returns (bool invalid) { + CompoundFeedDetails memory compDetails = s_feedDetails[aggregator]; + if (compDetails.deviationThresholdNumerator == 0) { + return false; + } + // Get both oracle price details + uint256 compPrice = _compoundPriceOf(compDetails.symbol); + (uint256 aggregatorPrice, uint8 aggregatorDecimals) = _aggregatorValues(aggregator); + + // Adjust the prices so the number of decimals in each align + (aggregatorPrice, compPrice) = _adjustPriceDecimals( + aggregatorPrice, + aggregatorDecimals, + compPrice, + compDetails.decimals + ); + + // Check whether the prices deviate beyond the threshold. + return _deviatesBeyondThreshold(aggregatorPrice, compPrice, compDetails.deviationThresholdNumerator); + } + + /** + * @notice Retrieve the price and the decimals from an Aggregator + * @param aggregator address + * @return price uint256 + * @return decimals uint8 + */ + function _aggregatorValues(address aggregator) private view returns (uint256 price, uint8 decimals) { + AggregatorV3Interface priceFeed = AggregatorV3Interface(aggregator); + (, int256 signedPrice, , , ) = priceFeed.latestRoundData(); + price = uint256(signedPrice); + decimals = priceFeed.decimals(); + } + + /** + * @notice Adjust the price values of the Aggregator and Compound feeds so that + * their decimal places align. This enables deviation to be calculated. + * @param aggregatorPrice uint256 + * @param aggregatorDecimals uint8 - decimal places included in the aggregator price + * @param compoundPrice uint256 + * @param compoundDecimals uint8 - decimal places included in the compound price + * @return adjustedAggregatorPrice uint256 + * @return adjustedCompoundPrice uint256 + */ + function _adjustPriceDecimals( + uint256 aggregatorPrice, + uint8 aggregatorDecimals, + uint256 compoundPrice, + uint8 compoundDecimals + ) private pure returns (uint256 adjustedAggregatorPrice, uint256 adjustedCompoundPrice) { + if (aggregatorDecimals > compoundDecimals) { + uint8 diff = aggregatorDecimals - compoundDecimals; + uint256 multiplier = 10**uint256(diff); + compoundPrice = compoundPrice * multiplier; + } else if (aggregatorDecimals < compoundDecimals) { + uint8 diff = compoundDecimals - aggregatorDecimals; + uint256 multiplier = 10**uint256(diff); + aggregatorPrice = aggregatorPrice * multiplier; + } + adjustedAggregatorPrice = aggregatorPrice; + adjustedCompoundPrice = compoundPrice; + } + + /** + * @notice Check whether the compound price deviates from the aggregator price + * beyond the given threshold + * @dev Prices must be adjusted to match decimals prior to calling this function + * @param aggregatorPrice uint256 + * @param compPrice uint256 + * @param deviationThresholdNumerator uint32 + * @return beyondThreshold boolean. Returns true if deviation is beyond threshold. + */ + function _deviatesBeyondThreshold( + uint256 aggregatorPrice, + uint256 compPrice, + uint32 deviationThresholdNumerator + ) private pure returns (bool beyondThreshold) { + // Deviation amount threshold from the aggregator price + uint256 deviationAmountThreshold = aggregatorPrice.mul(deviationThresholdNumerator).div(BILLION); + + // Calculate deviation + uint256 deviation; + if (aggregatorPrice > compPrice) { + deviation = aggregatorPrice.sub(compPrice); + } else if (aggregatorPrice < compPrice) { + deviation = compPrice.sub(aggregatorPrice); + } + beyondThreshold = (deviation >= deviationAmountThreshold); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/ConfirmedOwner.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/ConfirmedOwner.sol new file mode 100644 index 0000000..18e028e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/ConfirmedOwner.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./ConfirmedOwnerWithProposal.sol"; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwner is ConfirmedOwnerWithProposal { + + constructor( + address newOwner + ) + ConfirmedOwnerWithProposal( + newOwner, + address(0) + ) + { + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/ConfirmedOwnerWithProposal.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/ConfirmedOwnerWithProposal.sol new file mode 100644 index 0000000..63fc1e5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/ConfirmedOwnerWithProposal.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/OwnableInterface.sol"; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwnerWithProposal is OwnableInterface { + + address private s_owner; + address private s_pendingOwner; + + event OwnershipTransferRequested( + address indexed from, + address indexed to + ); + event OwnershipTransferred( + address indexed from, + address indexed to + ); + + constructor( + address owner, + address pendingOwner + ) { + require(owner != address(0), "Cannot set owner to zero"); + + s_owner = owner; + if (pendingOwner != address(0)) { + _transferOwnership(pendingOwner); + } + } + + /** + * @notice Allows an owner to begin transferring ownership to a new address, + * pending. + */ + function transferOwnership( + address to + ) + public + override + onlyOwner() + { + _transferOwnership(to); + } + + /** + * @notice Allows an ownership transfer to be completed by the recipient. + */ + function acceptOwnership() + external + override + { + require(msg.sender == s_pendingOwner, "Must be proposed owner"); + + address oldOwner = s_owner; + s_owner = msg.sender; + s_pendingOwner = address(0); + + emit OwnershipTransferred(oldOwner, msg.sender); + } + + /** + * @notice Get the current owner + */ + function owner() + public + view + override + returns ( + address + ) + { + return s_owner; + } + + /** + * @notice validate, transfer ownership, and emit relevant events + */ + function _transferOwnership( + address to + ) + private + { + require(to != msg.sender, "Cannot transfer to self"); + + s_pendingOwner = to; + + emit OwnershipTransferRequested(s_owner, to); + } + + /** + * @notice validate access + */ + function _validateOwnership() + internal + view + { + require(msg.sender == s_owner, "Only callable by owner"); + } + + /** + * @notice Reverts if called by anyone other than the contract owner. + */ + modifier onlyOwner() { + _validateOwnership(); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/Denominations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/Denominations.sol new file mode 100644 index 0000000..5371c98 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/Denominations.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.7.0; + +library Denominations { + address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 + address public constant USD = address(840); + address public constant GBP = address(826); + address public constant EUR = address(978); + address public constant JPY = address(392); + address public constant KRW = address(410); + address public constant CNY = address(156); + address public constant AUD = address(36); + address public constant CAD = address(124); + address public constant CHF = address(756); + address public constant ARS = address(32); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/LinkTokenReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/LinkTokenReceiver.sol new file mode 100644 index 0000000..3015791 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/LinkTokenReceiver.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +abstract contract LinkTokenReceiver { + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @dev The data payload's first 2 words will be overwritten by the `sender` and `amount` + * values to ensure correctness. Calls oracleRequest. + * @param sender Address of the sender + * @param amount Amount of LINK sent (specified in wei) + * @param data Payload of the transaction + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes memory data + ) + public + validateFromLINK() + permittedFunctionsForLINK(data) + { + assembly { + // solhint-disable-next-line avoid-low-level-calls + mstore(add(data, 36), sender) // ensure correct sender is passed + // solhint-disable-next-line avoid-low-level-calls + 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 + require(success, "Unable to create request"); + } + + function getChainlinkToken() + public + view + virtual + returns ( + address + ); + + /** + * @notice Validate the function called on token transfer + */ + function _validateTokenTransferAction( + bytes4 funcSelector, + bytes memory data + ) + internal + virtual; + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier validateFromLINK() { + require(msg.sender == getChainlinkToken(), "Must use LINK token"); + _; + } + + /** + * @dev Reverts if the given data does not begin with the `oracleRequest` function selector + * @param data The data payload of the request + */ + modifier permittedFunctionsForLINK( + bytes memory data + ) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(data, 32)) + } + _validateTokenTransferAction(funcSelector, data); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/Operator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/Operator.sol new file mode 100644 index 0000000..00c100c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/Operator.sol @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./AuthorizedReceiver.sol"; +import "./LinkTokenReceiver.sol"; +import "./ConfirmedOwner.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/OperatorInterface.sol"; +import "../interfaces/OwnableInterface.sol"; +import "../interfaces/WithdrawalInterface.sol"; +import "../vendor/Address.sol"; +import "../vendor/SafeMathChainlink.sol"; + +/** + * @title The Chainlink Operator contract + * @notice Node operators can deploy this contract to fulfill requests sent to them + */ +contract Operator is + AuthorizedReceiver, + ConfirmedOwner, + LinkTokenReceiver, + OperatorInterface, + WithdrawalInterface +{ + using Address for address; + using SafeMathChainlink for uint256; + + struct Commitment { + bytes31 paramsHash; + uint8 dataVersion; + } + + uint256 constant public getExpiryTime = 5 minutes; + uint256 constant private MAXIMUM_DATA_VERSION = 256; + uint256 constant private MINIMUM_CONSUMER_GAS_LIMIT = 400000; + uint256 constant private SELECTOR_LENGTH = 4; + uint256 constant private EXPECTED_REQUEST_WORDS = 2; + uint256 constant private MINIMUM_REQUEST_LENGTH = SELECTOR_LENGTH + (32 * EXPECTED_REQUEST_WORDS); + // We initialize fields to 1 instead of 0 so that the first invocation + // does not cost more gas. + uint256 constant private ONE_FOR_CONSISTENT_GAS_COST = 1; + // oracleRequest is version 1, enabling single word responses + bytes4 constant private ORACLE_REQUEST_SELECTOR = this.oracleRequest.selector; + // requestOracleData is version 2, enabling multi-word responses + bytes4 constant private OPERATOR_REQUEST_SELECTOR = this.requestOracleData.selector; + + LinkTokenInterface internal immutable linkToken; + mapping(bytes32 => Commitment) private s_commitments; + // Tokens sent for requests that have not been fulfilled yet + uint256 private s_tokensInEscrow = ONE_FOR_CONSISTENT_GAS_COST; + + 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 + ); + + event OracleResponse( + bytes32 indexed requestId + ); + + event OwnableContractAccepted( + address indexed accpetedContract + ); + + event TargetsUpdatedAuthorizedSenders( + address[] targets, + address[] senders, + address changedBy + ); + + /** + * @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 + * @param owner The address of the owner + */ + constructor( + address link, + address owner + ) + ConfirmedOwner(owner) + { + linkToken = LinkTokenInterface(link); // external but already deployed and unalterable + } + + function oracleRequest( + address sender, + uint256 payment, + bytes32 specId, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) + external + override + { + requestOracleData( + sender, + payment, + specId, + callbackAddress, + callbackFunctionId, + nonce, + dataVersion, + data + ); + } + + /** + * @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 requestOracleData( + address sender, + uint256 payment, + bytes32 specId, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) + public + override + validateFromLINK() + validateNotToLINK(callbackAddress) + { + (bytes32 requestId, uint256 expiration) = _verifyOracleRequest( + sender, + payment, + callbackAddress, + callbackFunctionId, + nonce, + dataVersion + ); + 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 payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + * @param data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) + external + override + validateAuthorizedSender() + validateRequestId(requestId) + returns ( + bool + ) + { + _verifyOracleResponse( + requestId, + payment, + callbackAddress, + callbackFunctionId, + expiration, + 1 + ); + emit OracleResponse(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, ) = callbackAddress.call(abi.encodeWithSelector(callbackFunctionId, requestId, data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Called by the Chainlink node to fulfill requests with multi-word support + * @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 payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + * @param data The data to return to the consuming contract + * @return Status if the external call was successful + */ + function fulfillOracleRequest2( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes calldata data + ) + external + override + validateAuthorizedSender() + validateRequestId(requestId) + validateMultiWordResponseId(requestId, data) + returns ( + bool + ) + { + _verifyOracleResponse( + requestId, + payment, + callbackAddress, + callbackFunctionId, + expiration, + 2 + ); + emit OracleResponse(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, ) = callbackAddress.call(abi.encodePacked(callbackFunctionId, data)); // solhint-disable-line avoid-low-level-calls + return success; + } + + /** + * @notice Transfer the ownership of ownable contracts + * @param ownable list of addresses to transfer + * @param newOwner address to transfer ownership to + */ + function transferOwnableContracts( + address[] calldata ownable, + address newOwner + ) + external + onlyOwner() + { + for (uint256 i = 0; i < ownable.length; i++) { + OwnableInterface(ownable[i]).transferOwnership(newOwner); + } + } + + /** + * @notice Accept the ownership of an ownable contract + * @dev Must be the pending owner on the contract + * @param ownable list of addresses of Ownable contracts to accept + */ + function acceptOwnableContracts( + address[] calldata ownable + ) + public + validateAuthorizedSenderSetter() + { + for (uint256 i = 0; i < ownable.length; i++) { + OwnableInterface(ownable[i]).acceptOwnership(); + emit OwnableContractAccepted(ownable[i]); + } + } + + /** + * @notice Sets the fulfillment permission for + * @param targets The addresses to set permissions on + * @param senders The addresses that are allowed to send updates + */ + function setAuthorizedSendersOn( + address[] calldata targets, + address[] calldata senders + ) + public + validateAuthorizedSenderSetter() + { + TargetsUpdatedAuthorizedSenders(targets, senders, msg.sender); + + for (uint256 i = 0; i < targets.length; i++) { + AuthorizedReceiverInterface(targets[i]).setAuthorizedSenders(senders); + } + } + + /** + * @notice Sets the fulfillment permission for + * @param targets The addresses to set permissions on + * @param senders The addresses that are allowed to send updates + */ + function acceptAuthorizedReceivers( + address[] calldata targets, + address[] calldata senders + ) + external + validateAuthorizedSenderSetter() + { + acceptOwnableContracts(targets); + setAuthorizedSendersOn(targets, senders); + } + + /** + * @notice Allows the node operator to withdraw earned LINK to a given address + * @dev The owner of the contract can be another wallet and does not have to be a Chainlink node + * @param recipient The address to send the LINK token to + * @param amount The amount to send (specified in wei) + */ + function withdraw( + address recipient, + uint256 amount + ) + external + override(OracleInterface, WithdrawalInterface) + onlyOwner() + validateAvailableFunds(amount) + { + assert(linkToken.transfer(recipient, amount)); + } + + /** + * @notice Displays the amount of LINK that is available for the node operator to withdraw + * @dev We use `ONE_FOR_CONSISTENT_GAS_COST` in place of 0 in storage + * @return The amount of withdrawable LINK on the contract + */ + function withdrawable() + external + view + override(OracleInterface, WithdrawalInterface) + returns (uint256) + { + return _fundsAvailable(); + } + + /** + * @notice Forward a call to another contract + * @dev Only callable by the owner + * @param to address + * @param data to forward + */ + function ownerForward( + address to, + bytes calldata data + ) + external + onlyOwner() + validateNotToLINK(to) + { + require(to.isContract(), "Must forward to a contract"); + (bool status,) = to.call(data); + require(status, "Forwarded call failed"); + } + + /** + * @notice Interact with other LinkTokenReceiver contracts by calling transferAndCall + * @param to The address to transfer to. + * @param value The amount to be transferred. + * @param data The extra data to be passed to the receiving contract. + * @return success bool + */ + function ownerTransferAndCall( + address to, + uint256 value, + bytes calldata data + ) + external + override + onlyOwner() + validateAvailableFunds(value) + returns ( + bool success + ) + { + return linkToken.transferAndCall(to, value, data); + } + + /** + * @notice Distribute funds to multiple addresses using ETH send + * to this payable function. + * @dev Array length must be equal, ETH sent must equal the sum of amounts. + * @param receivers list of addresses + * @param amounts list of amounts + */ + function distributeFunds( + address payable[] calldata receivers, + uint[] calldata amounts + ) + external + payable + { + require(receivers.length > 0 && receivers.length == amounts.length, "Invalid array length(s)"); + uint256 valueRemaining = msg.value; + for (uint256 i = 0; i < receivers.length; i++) { + uint256 sendAmount = amounts[i]; + valueRemaining = valueRemaining.sub(sendAmount); + receivers[i].transfer(sendAmount); + } + require(valueRemaining == 0, "Too much ETH sent"); + } + + /** + * @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 callbackFunc The requester's specified callback address + * @param expiration The time of the expiration for the request + */ + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunc, + uint256 expiration + ) + external + override + { + bytes31 paramsHash = _buildFunctionHash(payment, msg.sender, callbackFunc, expiration); + require(s_commitments[requestId].paramsHash == paramsHash, "Params do not match request ID"); + // solhint-disable-next-line not-rely-on-time + require(expiration <= block.timestamp, "Request is not expired"); + + delete s_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); + } + + + /** + * @notice Require that the token transfer action is valid + * @dev OPERATOR_REQUEST_SELECTOR = multiword, ORACLE_REQUEST_SELECTOR = singleword + */ + function _validateTokenTransferAction( + bytes4 funcSelector, + bytes memory data + ) + internal + override + pure + { + require(data.length >= MINIMUM_REQUEST_LENGTH, "Invalid request length"); + require(funcSelector == OPERATOR_REQUEST_SELECTOR || funcSelector == ORACLE_REQUEST_SELECTOR, "Must use whitelisted functions"); + } + + /** + * @notice Verify the Oracle Request + * @param sender The sender of the request + * @param payment The amount of payment given (specified in wei) + * @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 + */ + function _verifyOracleRequest( + address sender, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion + ) + private + returns ( + bytes32 requestId, + uint256 expiration + ) + { + requestId = keccak256(abi.encodePacked(sender, nonce)); + require(s_commitments[requestId].paramsHash == 0, "Must use a unique ID"); + // solhint-disable-next-line not-rely-on-time + expiration = block.timestamp.add(getExpiryTime); + bytes31 paramsHash = _buildFunctionHash(payment, callbackAddress, callbackFunctionId, expiration); + s_commitments[requestId] = Commitment(paramsHash, _safeCastToUint8(dataVersion)); + s_tokensInEscrow = s_tokensInEscrow.add(payment); + return (requestId, expiration); + } + + /** + * @notice Verify the Oracle Response + * @param requestId The fulfillment request ID that must match the requester's + * @param payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + */ + function _verifyOracleResponse( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + uint256 dataVersion + ) + internal + { + bytes31 paramsHash = _buildFunctionHash(payment, callbackAddress, callbackFunctionId, expiration); + require(s_commitments[requestId].paramsHash == paramsHash, "Params do not match request ID"); + require(s_commitments[requestId].dataVersion <= _safeCastToUint8(dataVersion), "Data versions must match"); + s_tokensInEscrow = s_tokensInEscrow.sub(payment); + delete s_commitments[requestId]; + } + + /** + * @notice Build the bytes31 function hash from the payment, callback and expiration. + * @param payment The payment amount that will be released for the oracle (specified in wei) + * @param callbackAddress The callback address to call for fulfillment + * @param callbackFunctionId The callback function ID to use for fulfillment + * @param expiration The expiration that the node should respond by before the requester can cancel + * @return hash bytes31 + */ + function _buildFunctionHash( + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration + ) + internal + pure + returns ( + bytes31 + ) + { + return bytes31(keccak256( + abi.encodePacked( + payment, + callbackAddress, + callbackFunctionId, + expiration + ) + )); + } + + /** + * @notice Safely cast uint256 to uint8 + * @param number uint256 + * @return uint8 number + */ + function _safeCastToUint8( + uint256 number + ) + internal + pure + returns ( + uint8 + ) + { + require(number < MAXIMUM_DATA_VERSION, "number too big to cast"); + return uint8(number); + } + + /** + * @notice Returns the LINK available in this contract, not locked in escrow + * @return uint256 LINK tokens available + */ + function _fundsAvailable() + private + view + returns ( + uint256 + ) + { + uint256 inEscrow = s_tokensInEscrow.sub(ONE_FOR_CONSISTENT_GAS_COST); + return linkToken.balanceOf(address(this)).sub(inEscrow); + } + + /** + * @notice concrete implementation of AuthorizedReceiver + * @return bool of whether sender is authorized + */ + function _canSetAuthorizedSenders() + internal + view + override + returns (bool) + { + return isAuthorizedSender(msg.sender) || owner() == msg.sender; + } + + + // MODIFIERS + + /** + * @dev Reverts if the first 32 bytes of the bytes array is not equal to requestId + * @param requestId bytes32 + * @param data bytes + */ + modifier validateMultiWordResponseId( + bytes32 requestId, + bytes memory data + ) { + bytes32 firstWord; + assembly{ + firstWord := mload(add(data, 0x20)) + } + require(requestId == firstWord, "First word must be requestId"); + _; + } + + /** + * @dev Reverts if amount requested is greater than withdrawable balance + * @param amount The given amount to compare to `s_withdrawableTokens` + */ + modifier validateAvailableFunds( + uint256 amount + ) { + require(_fundsAvailable() >= amount, "Amount requested is greater than withdrawable balance"); + _; + } + + /** + * @dev Reverts if request ID does not exist + * @param requestId The given request ID to check in stored `commitments` + */ + modifier validateRequestId( + bytes32 requestId + ) { + require(s_commitments[requestId].paramsHash != 0, "Must have a valid requestId"); + _; + } + + /** + * @dev Reverts if the callback address is the LINK token + * @param to The callback address + */ + modifier validateNotToLINK( + address to + ) { + require(to != address(linkToken), "Cannot call to LINK"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/OperatorFactory.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/OperatorFactory.sol new file mode 100644 index 0000000..d44bc4f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/OperatorFactory.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./Operator.sol"; +import "./AuthorizedForwarder.sol"; + +/** + * @title Operator Factory + * @notice Creates Operator contracts for node operators + */ +contract OperatorFactory { + + address public immutable getChainlinkToken; + mapping(address => bool) private s_created; + + event OperatorCreated( + address indexed operator, + address indexed owner, + address indexed sender + ); + event AuthorizedForwarderCreated( + address indexed forwarder, + address indexed owner, + address indexed sender + ); + + /** + * @param linkAddress address + */ + constructor( + address linkAddress + ) { + getChainlinkToken = linkAddress; + } + + /** + * @notice creates a new Operator contract with the msg.sender as owner + */ + function deployNewOperator() + external + returns ( + address + ) + { + Operator operator = new Operator( + getChainlinkToken, + msg.sender + ); + + s_created[address(operator)] = true; + emit OperatorCreated( + address(operator), + msg.sender, + msg.sender + ); + + return address(operator); + } + + /** + * @notice creates a new Operator contract with the msg.sender as owner and a + * new Operator Forwarder with the Operator as the owner + */ + function deployNewOperatorAndForwarder() + external + returns ( + address, + address + ) + { + Operator operator = new Operator( + getChainlinkToken, + msg.sender + ); + s_created[address(operator)] = true; + emit OperatorCreated( + address(operator), + msg.sender, + msg.sender + ); + + bytes memory tmp = new bytes(0); + AuthorizedForwarder forwarder = new AuthorizedForwarder( + getChainlinkToken, + address(operator), + address(0), + tmp + ); + s_created[address(forwarder)] = true; + emit AuthorizedForwarderCreated( + address(forwarder), + address(operator), + msg.sender + ); + + return (address(operator), address(forwarder)); + } + + /** + * @notice creates a new Forwarder contract with the msg.sender as owner + */ + function deployNewForwarder() + external + returns ( + address + ) + { + bytes memory tmp = new bytes(0); + AuthorizedForwarder forwarder = new AuthorizedForwarder( + getChainlinkToken, + msg.sender, + address(0), + tmp + ); + + s_created[address(forwarder)] = true; + emit AuthorizedForwarderCreated( + address(forwarder), + msg.sender, + msg.sender + ); + + return address(forwarder); + } + + /** + * @notice creates a new Forwarder contract with the msg.sender as owner + */ + function deployNewForwarderAndTransferOwnership( + address to, + bytes calldata message + ) + external + returns ( + address + ) + { + AuthorizedForwarder forwarder = new AuthorizedForwarder( + getChainlinkToken, + msg.sender, + to, + message + ); + + s_created[address(forwarder)] = true; + emit AuthorizedForwarderCreated( + address(forwarder), + msg.sender, + msg.sender + ); + + return address(forwarder); + } + + /** + * @notice indicates whether this factory deployed an address + */ + function created( + address query + ) + external + view + returns (bool) + { + return s_created[query]; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/OperatorForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/OperatorForwarder.sol new file mode 100644 index 0000000..605d80b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/OperatorForwarder.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/OperatorInterface.sol"; +import "./ConfirmedOwner.sol"; + +contract OperatorForwarder { + address public immutable authorizedSender1; + address public immutable authorizedSender2; + address public immutable authorizedSender3; + + address public immutable linkAddr; + + constructor(address link) { + linkAddr = link; + authorizedSender1 = ConfirmedOwner(msg.sender).owner(); + address[] memory authorizedSenders = OperatorInterface(msg.sender).getAuthorizedSenders(); + authorizedSender2 = (authorizedSenders.length > 0) ? authorizedSenders[0] : address(0); + authorizedSender3 = (authorizedSenders.length > 1) ? authorizedSenders[1] : address(0); + } + + function forward( + address to, + bytes calldata data + ) + public + onlyAuthorizedSender() + { + require(to != linkAddr, "Cannot #forward to Link token"); + (bool status,) = to.call(data); + require(status, "Forwarded call failed."); + } + + modifier onlyAuthorizedSender() { + require(msg.sender == authorizedSender1 + || msg.sender == authorizedSender2 + || msg.sender == authorizedSender3, + "Not authorized to fulfill requests" + ); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/StalenessFlaggingValidator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/StalenessFlaggingValidator.sol new file mode 100644 index 0000000..070ff47 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/StalenessFlaggingValidator.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../ConfirmedOwner.sol"; +import "../vendor/SafeMathChainlink.sol"; +import "../interfaces/FlagsInterface.sol"; +import "../interfaces/AggregatorV3Interface.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; + +contract StalenessFlaggingValidator is ConfirmedOwner, KeeperCompatibleInterface { + using SafeMathChainlink for uint256; + + FlagsInterface private s_flags; + mapping(address => uint256) private s_thresholdSeconds; + + event FlagsAddressUpdated(address indexed previous, address indexed current); + event FlaggingThresholdUpdated(address indexed aggregator, uint256 indexed previous, uint256 indexed current); + + /** + * @notice Create a new StalenessFlaggingValidator + * @param flagsAddress Address of the flag contract + * @dev Ensure that this contract has sufficient write permissions + * on the flag contract + */ + constructor(address flagsAddress) ConfirmedOwner(msg.sender) { + setFlagsAddress(flagsAddress); + } + + /** + * @notice Updates the flagging contract address for raising flags + * @param flagsAddress sets the address of the flags contract + */ + function setFlagsAddress(address flagsAddress) public onlyOwner { + address previous = address(s_flags); + if (previous != flagsAddress) { + s_flags = FlagsInterface(flagsAddress); + emit FlagsAddressUpdated(previous, flagsAddress); + } + } + + /** + * @notice Set the threshold limits for each aggregator + * @dev parameters must be same length + * @param aggregators address[] memory + * @param flaggingThresholds uint256[] memory + */ + function setThresholds(address[] memory aggregators, uint256[] memory flaggingThresholds) public onlyOwner { + require(aggregators.length == flaggingThresholds.length, "Different sized arrays"); + for (uint256 i = 0; i < aggregators.length; i++) { + address aggregator = aggregators[i]; + uint256 previousThreshold = s_thresholdSeconds[aggregator]; + uint256 newThreshold = flaggingThresholds[i]; + if (previousThreshold != newThreshold) { + s_thresholdSeconds[aggregator] = newThreshold; + emit FlaggingThresholdUpdated(aggregator, previousThreshold, newThreshold); + } + } + } + + /** + * @notice Check for staleness in an array of aggregators + * @dev If any of the aggregators are stale, this function will return true, + * otherwise false + * @param aggregators address[] memory + * @return address[] memory stale aggregators + */ + function check(address[] memory aggregators) public view returns (address[] memory) { + uint256 currentTimestamp = block.timestamp; + address[] memory staleAggregators = new address[](aggregators.length); + uint256 staleCount = 0; + for (uint256 i = 0; i < aggregators.length; i++) { + address aggregator = aggregators[i]; + if (isStale(aggregator, currentTimestamp)) { + staleAggregators[staleCount] = aggregator; + staleCount++; + } + } + + if (aggregators.length != staleCount) { + assembly { + mstore(staleAggregators, staleCount) + } + } + return staleAggregators; + } + + /** + * @notice Check for staleness in an array of aggregators, raise a flag + * on the flags contract for each aggregator that is stale + * @dev This contract must have write permissions on the flags contract + * @param aggregators address[] memory + * @return address[] memory stale aggregators + */ + function update(address[] memory aggregators) public returns (address[] memory) { + address[] memory staleAggregators = check(aggregators); + s_flags.raiseFlags(staleAggregators); + return staleAggregators; + } + + /** + * @notice Check for staleness in an array of aggregators + * @dev Overriding KeeperInterface + * @param data bytes encoded address array + * @return needsUpkeep bool indicating whether upkeep needs to be performed + * @return staleAggregators bytes encoded address array of stale aggregator addresses + */ + function checkUpkeep(bytes calldata data) external view override returns (bool, bytes memory) { + address[] memory staleAggregators = check(abi.decode(data, (address[]))); + bool needsUpkeep = (staleAggregators.length > 0); + return (needsUpkeep, abi.encode(staleAggregators)); + } + + /** + * @notice Check for staleness in an array of aggregators, raise a flag + * on the flags contract for each aggregator that is stale + * @dev Overriding KeeperInterface + * @param data bytes encoded address array + */ + function performUpkeep(bytes calldata data) external override { + update(abi.decode(data, (address[]))); + } + + /** + * @notice Get the threshold of an aggregator + * @param aggregator address + * @return uint256 + */ + function threshold(address aggregator) external view returns (uint256) { + return s_thresholdSeconds[aggregator]; + } + + /** + * @notice Get the flags address + * @return address + */ + function flags() external view returns (address) { + return address(s_flags); + } + + /** + * @notice Check if an aggregator is stale. + * @dev Staleness is where an aggregator's `updatedAt` field is older + * than the threshold set for it in this contract + * @param aggregator address + * @param currentTimestamp uint256 + * @return stale bool + */ + function isStale(address aggregator, uint256 currentTimestamp) private view returns (bool stale) { + if (s_thresholdSeconds[aggregator] == 0) { + return false; + } + (, , , uint256 updatedAt, ) = AggregatorV3Interface(aggregator).latestRoundData(); + uint256 diff = currentTimestamp.sub(updatedAt); + if (diff > s_thresholdSeconds[aggregator]) { + return true; + } + return false; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/VRFConsumerBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/VRFConsumerBase.sol new file mode 100644 index 0000000..0a3b74f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/VRFConsumerBase.sol @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../vendor/SafeMathChainlink.sol"; + +import "../interfaces/LinkTokenInterface.sol"; + +import "./VRFRequestIDBase.sol"; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constuctor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator, _link) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash), and have told you the minimum LINK + * @dev price for VRF service. Make sure your contract has sufficient LINK, and + * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you + * @dev want to generate randomness from. + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomness method. + * + * @dev The randomness argument to fulfillRandomness is the actual random value + * @dev generated from your seed. + * + * @dev The requestId argument is generated from the keyHash and the seed by + * @dev makeRequestId(keyHash, seed). If your contract could have concurrent + * @dev requests open, you can use the requestId to track which seed is + * @dev associated with which randomness. See VRFRequestIDBase.sol for more + * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously.) + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. (Which is critical to making unpredictable randomness! See the + * @dev next section.) + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the ultimate input to the VRF is mixed with the block hash of the + * @dev block in which the request is made, user-provided seeds have no impact + * @dev on its economic security properties. They are only included for API + * @dev compatability with previous versions of this contract. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. + */ +abstract contract VRFConsumerBase is VRFRequestIDBase { + + using SafeMathChainlink for uint256; + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBase expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomness the VRF output + */ + function fulfillRandomness( + bytes32 requestId, + uint256 randomness + ) + internal + virtual; + + /** + * @dev In order to keep backwards compatibility we have kept the user + * seed field around. We remove the use of it because given that the blockhash + * enters later, it overrides whatever randomness the used seed provides. + * Given that it adds no security, and can easily lead to misunderstandings, + * we have removed it from usage and can now provide a simpler API. + */ + uint256 constant private USER_SEED_PLACEHOLDER = 0; + + /** + * @notice requestRandomness initiates a request for VRF output given _seed + * + * @dev The fulfillRandomness method receives the output, once it's provided + * @dev by the Oracle, and verified by the vrfCoordinator. + * + * @dev The _keyHash must already be registered with the VRFCoordinator, and + * @dev the _fee must exceed the fee specified during registration of the + * @dev _keyHash. + * + * @dev The _seed parameter is vestigial, and is kept only for API + * @dev compatibility with older versions. It can't *hurt* to mix in some of + * @dev your own randomness, here, but it's not necessary because the VRF + * @dev oracle will mix the hash of the block containing your request into the + * @dev VRF seed it ultimately uses. + * + * @param _keyHash ID of public key against which randomness is generated + * @param _fee The amount of LINK to send with the request + * + * @return requestId unique ID for this request + * + * @dev The returned requestId can be used to distinguish responses to + * @dev concurrent requests. It is passed as the first argument to + * @dev fulfillRandomness. + */ + function requestRandomness( + bytes32 _keyHash, + uint256 _fee + ) + internal + returns ( + bytes32 requestId + ) + { + LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); + // This is the seed passed to VRFCoordinator. The oracle will mix this with + // the hash of the block containing this request to obtain the seed/input + // which is finally passed to the VRF cryptographic machinery. + uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); + // nonces[_keyHash] must stay in sync with + // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above + // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). + // This provides protection against the user repeating their input seed, + // which would result in a predictable/duplicate output, if multiple such + // requests appeared in the same block. + nonces[_keyHash] = nonces[_keyHash].add(1); + return makeRequestId(_keyHash, vRFSeed); + } + + LinkTokenInterface immutable internal LINK; + address immutable private vrfCoordinator; + + // Nonces for each VRF key from which randomness has been requested. + // + // Must stay in sync with VRFCoordinator[_keyHash][this] + mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + * @param _link address of LINK token contract + * + * @dev https://docs.chain.link/docs/link-token-contracts + */ + constructor( + address _vrfCoordinator, + address _link + ) { + vrfCoordinator = _vrfCoordinator; + LINK = LinkTokenInterface(_link); + } + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomness( + bytes32 requestId, + uint256 randomness + ) + external + { + require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); + fulfillRandomness(requestId, randomness); + } +} + diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/VRFRequestIDBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/VRFRequestIDBase.sol new file mode 100644 index 0000000..c6dcdfa --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/dev/VRFRequestIDBase.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract VRFRequestIDBase { + + /** + * @notice returns the seed which is actually input to the VRF coordinator + * + * @dev To prevent repetition of VRF output due to repetition of the + * @dev user-supplied seed, that seed is combined in a hash with the + * @dev user-specific nonce, and the address of the consuming contract. The + * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in + * @dev the final seed, but the nonce does protect against repetition in + * @dev requests which are included in a single block. + * + * @param _userSeed VRF seed input provided by user + * @param _requester Address of the requesting contract + * @param _nonce User-specific nonce at the time of the request + */ + function makeVRFInputSeed( + bytes32 _keyHash, + uint256 _userSeed, + address _requester, + uint256 _nonce + ) + internal + pure + returns ( + uint256 + ) + { + return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); + } + + /** + * @notice Returns the id for this request + * @param _keyHash The serviceAgreement ID to be used for this request + * @param _vRFInputSeed The seed to be passed directly to the VRF + * @return The id for this request + * + * @dev Note that _vRFInputSeed is not the seed passed by the consuming + * @dev contract, but the one generated by makeVRFInputSeed + */ + function makeRequestId( + bytes32 _keyHash, + uint256 _vRFInputSeed + ) + internal + pure + returns ( + bytes32 + ) + { + return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorInterface.sol new file mode 100644 index 0000000..8eddc4a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorInterface.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AggregatorInterface { + function latestAnswer() external view returns (int256); + + function latestTimestamp() external view returns (uint256); + + function latestRound() external view returns (uint256); + + function getAnswer(uint256 roundId) external view returns (int256); + + function getTimestamp(uint256 roundId) external view returns (uint256); + + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); + + event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorProxyInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorProxyInterface.sol new file mode 100644 index 0000000..a56f5eb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorProxyInterface.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./AggregatorV2V3Interface.sol"; + +interface AggregatorProxyInterface is AggregatorV2V3Interface { + function phaseAggregators(uint16 phaseId) external view returns (address); + + function phaseId() external view returns (uint16); + + function proposedAggregator() external view returns (address); + + function proposedGetRoundData(uint80 roundId) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function proposedLatestRoundData() + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function aggregator() external view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorV2V3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorV2V3Interface.sol new file mode 100644 index 0000000..a035368 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorV2V3Interface.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./AggregatorInterface.sol"; +import "./AggregatorV3Interface.sol"; + +interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorV3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorV3Interface.sol new file mode 100644 index 0000000..06b7b1a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AggregatorV3Interface.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AggregatorV3Interface { + function decimals() external view returns (uint8); + + function description() external view returns (string memory); + + function version() external view returns (uint256); + + // getRoundData and latestRoundData should both raise "No data present" + // if they do not have data to report, instead of returning unset values + // which could be misinterpreted as actual reported values. + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AuthorizedReceiverInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AuthorizedReceiverInterface.sol new file mode 100644 index 0000000..aec2c03 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/AuthorizedReceiverInterface.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AuthorizedReceiverInterface { + function isAuthorizedSender(address sender) external view returns (bool); + + function getAuthorizedSenders() external returns (address[] memory); + + function setAuthorizedSenders(address[] calldata senders) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/ChainlinkRequestInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/ChainlinkRequestInterface.sol new file mode 100644 index 0000000..77d2b6e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/ChainlinkRequestInterface.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface ChainlinkRequestInterface { + function oracleRequest( + address sender, + uint256 requestPrice, + bytes32 serviceAgreementID, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external; + + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunctionId, + uint256 expiration + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/ENSInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/ENSInterface.sol new file mode 100644 index 0000000..84fd654 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/ENSInterface.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface ENSInterface { + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + function setSubnodeOwner( + bytes32 node, + bytes32 label, + address owner + ) external; + + function setResolver(bytes32 node, address resolver) external; + + function setOwner(bytes32 node, address owner) external; + + function setTTL(bytes32 node, uint64 ttl) external; + + function owner(bytes32 node) external view returns (address); + + function resolver(bytes32 node) external view returns (address); + + function ttl(bytes32 node) external view returns (uint64); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/FeedRegistryInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/FeedRegistryInterface.sol new file mode 100644 index 0000000..43f5fd7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/FeedRegistryInterface.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import "./AggregatorV2V3Interface.sol"; + +interface FeedRegistryInterface { + struct Phase { + uint16 phaseId; + uint80 startingAggregatorRoundId; + uint80 endingAggregatorRoundId; + } + + event FeedProposed( + address indexed asset, + address indexed denomination, + address indexed proposedAggregator, + address currentAggregator, + address sender + ); + event FeedConfirmed( + address indexed asset, + address indexed denomination, + address indexed latestAggregator, + address previousAggregator, + uint16 nextPhaseId, + address sender + ); + + // V3 AggregatorV3Interface + + function decimals(address base, address quote) external view returns (uint8); + + function description(address base, address quote) external view returns (string memory); + + function version(address base, address quote) external view returns (uint256); + + function latestRoundData(address base, address quote) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function getRoundData( + address base, + address quote, + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + // V2 AggregatorInterface + + function latestAnswer(address base, address quote) external view returns (int256 answer); + + function latestTimestamp(address base, address quote) external view returns (uint256 timestamp); + + function latestRound(address base, address quote) external view returns (uint256 roundId); + + function getAnswer( + address base, + address quote, + uint256 roundId + ) external view returns (int256 answer); + + function getTimestamp( + address base, + address quote, + uint256 roundId + ) external view returns (uint256 timestamp); + + // Registry getters + + function getFeed(address base, address quote) external view returns (AggregatorV2V3Interface aggregator); + + function getPhaseFeed( + address base, + address quote, + uint16 phaseId + ) external view returns (AggregatorV2V3Interface aggregator); + + function isFeedEnabled(address aggregator) external view returns (bool); + + function getPhase( + address base, + address quote, + uint16 phaseId + ) external view returns (Phase memory phase); + + // Round helpers + + function getRoundFeed( + address base, + address quote, + uint80 roundId + ) external view returns (AggregatorV2V3Interface aggregator); + + function getPhaseRange( + address base, + address quote, + uint16 phaseId + ) external view returns (uint80 startingRoundId, uint80 endingRoundId); + + function getPreviousRoundId( + address base, + address quote, + uint80 roundId + ) external view returns (uint80 previousRoundId); + + function getNextRoundId( + address base, + address quote, + uint80 roundId + ) external view returns (uint80 nextRoundId); + + // Feed management + + function proposeFeed( + address base, + address quote, + address aggregator + ) external; + + function confirmFeed( + address base, + address quote, + address aggregator + ) external; + + // Proposed aggregator + + function getProposedFeed(address base, address quote) + external + view + returns (AggregatorV2V3Interface proposedAggregator); + + function proposedGetRoundData( + address base, + address quote, + uint80 roundId + ) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function proposedLatestRoundData(address base, address quote) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + // Phases + function getCurrentPhaseId(address base, address quote) external view returns (uint16 currentPhaseId); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/FlagsInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/FlagsInterface.sol new file mode 100644 index 0000000..157d1ed --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/FlagsInterface.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface FlagsInterface { + function getFlag(address) external view returns (bool); + + function getFlags(address[] calldata) external view returns (bool[] memory); + + function raiseFlag(address) external; + + function raiseFlags(address[] calldata) external; + + function lowerFlags(address[] calldata) external; + + function setRaisingAccessController(address) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/KeeperCompatibleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/KeeperCompatibleInterface.sol new file mode 100644 index 0000000..c14de02 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/KeeperCompatibleInterface.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.7.0; + +interface KeeperCompatibleInterface { + /** + * @notice method that is simulated by the keepers to see if any work actually + * needs to be performed. This method does does not actually need to be + * executable, and since it is only ever simulated it can consume lots of gas. + * @dev To ensure that it is never called, you may want to add the + * cannotExecute modifier from KeeperBase to your implementation of this + * method. + * @param checkData specified in the upkeep registration so it is always the + * same for a registered upkeep. This can easily be broken down into specific + * arguments using `abi.decode`, so multiple upkeeps can be registered on the + * same contract and easily differentiated by the contract. + * @return upkeepNeeded boolean to indicate whether the keeper should call + * performUpkeep or not. + * @return performData bytes that the keeper should call performUpkeep with, if + * upkeep is needed. If you would like to encode data to decode later, try + * `abi.encode`. + */ + function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData); + + /** + * @notice method that is actually executed by the keepers, via the registry. + * The data returned by the checkUpkeep simulation will be passed into + * this method to actually be executed. + * @dev The input to this method should not be trusted, and the caller of the + * method should not even be restricted to any single registry. Anyone should + * be able call it, and the input should be validated, there is no guarantee + * that the data passed in is the performData returned from checkUpkeep. This + * could happen due to malicious keepers, racing keepers, or simply a state + * change while the performUpkeep transaction is waiting for confirmation. + * Always validate the data passed in. + * @param performData is the data which was passed back from the checkData + * simulation. If it is encoded, it can easily be decoded into other types by + * calling `abi.decode`. This data should not be trusted, and should be + * validated against the contract's current state. + */ + function performUpkeep(bytes calldata performData) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/KeeperRegistryInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/KeeperRegistryInterface.sol new file mode 100644 index 0000000..df5ea92 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/KeeperRegistryInterface.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.7.0; + +interface KeeperRegistryBaseInterface { + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id); + + function performUpkeep(uint256 id, bytes calldata performData) external returns (bool success); + + function cancelUpkeep(uint256 id) external; + + function addFunds(uint256 id, uint96 amount) external; + + function getUpkeep(uint256 id) + external + view + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber + ); + + function getUpkeepCount() external view returns (uint256); + + function getCanceledUpkeepList() external view returns (uint256[] memory); + + function getKeeperList() external view returns (address[] memory); + + function getKeeperInfo(address query) + external + view + returns ( + address payee, + bool active, + uint96 balance + ); + + function getConfig() + external + view + returns ( + uint32 paymentPremiumPPB, + uint24 checkFrequencyBlocks, + uint32 checkGasLimit, + uint24 stalenessSeconds, + uint16 gasCeilingMultiplier, + uint256 fallbackGasPrice, + uint256 fallbackLinkPrice + ); +} + +/** + * @dev The view methods are not actually marked as view in the implementation + * but we want them to be easily queried off-chain. Solidity will not compile + * if we actually inherit from this interface, so we document it here. + */ +interface KeeperRegistryInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + view + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + int256 gasWei, + int256 linkEth + ); +} + +interface KeeperRegistryExecutableInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/LinkTokenInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/LinkTokenInterface.sol new file mode 100644 index 0000000..d9b59c7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/LinkTokenInterface.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +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); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OperatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OperatorInterface.sol new file mode 100644 index 0000000..2a76a8b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OperatorInterface.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./ChainlinkRequestInterface.sol"; +import "./OracleInterface.sol"; + +interface OperatorInterface is ChainlinkRequestInterface, OracleInterface { + function operatorRequest( + address sender, + uint256 payment, + bytes32 specId, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external; + + function fulfillOracleRequest2( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes calldata data + ) external returns (bool); + + function ownerTransferAndCall( + address to, + uint256 value, + bytes calldata data + ) external returns (bool success); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OracleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OracleInterface.sol new file mode 100644 index 0000000..bf54fc0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OracleInterface.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface OracleInterface { + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) external returns (bool); + + function withdraw(address recipient, uint256 amount) external; + + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OwnableInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OwnableInterface.sol new file mode 100644 index 0000000..9490065 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/OwnableInterface.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface OwnableInterface { + function owner() external returns (address); + + function transferOwnership(address recipient) external; + + function acceptOwnership() external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/PointerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/PointerInterface.sol new file mode 100644 index 0000000..ee3d8ae --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/PointerInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface PointerInterface { + function getAddress() external view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/TypeAndVersionInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/TypeAndVersionInterface.sol new file mode 100644 index 0000000..6adff62 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/TypeAndVersionInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +abstract contract TypeAndVersionInterface { + function typeAndVersion() external pure virtual returns (string memory); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/UniswapAnchoredView.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/UniswapAnchoredView.sol new file mode 100644 index 0000000..f750cc2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/UniswapAnchoredView.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +// Compound Finance's oracle interface +interface UniswapAnchoredView { + function price(string memory symbol) external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/UpkeepInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/UpkeepInterface.sol new file mode 100644 index 0000000..fe7e194 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/UpkeepInterface.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface UpkeepInterface { + function checkUpkeep( + bytes calldata data + ) + external + returns ( + bool, + bytes memory + ); + + function performUpkeep( + bytes calldata data + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/WithdrawalInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/WithdrawalInterface.sol new file mode 100644 index 0000000..8049850 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/interfaces/WithdrawalInterface.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface WithdrawalInterface { + /** + * @notice transfer LINK held by the contract belonging to msg.sender to + * another address + * @param recipient is the address to send the LINK to + * @param amount is the amount of LINK to send + */ + function withdraw(address recipient, uint256 amount) external; + + /** + * @notice query the available amount of LINK to withdraw by msg.sender + */ + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ChainlinkClientTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ChainlinkClientTestHelper.sol new file mode 100644 index 0000000..5d9afb2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ChainlinkClientTestHelper.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../ChainlinkClient.sol"; +import "../vendor/SafeMathChainlink.sol"; + +contract ChainlinkClientTestHelper is ChainlinkClient { + using SafeMathChainlink for uint256; + + constructor(address _link, address _oracle) { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + } + + event Request(bytes32 id, address callbackAddress, bytes4 callbackfunctionSelector, bytes data); + event LinkAmount(uint256 amount); + + function publicNewRequest( + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature + ) public { + Chainlink.Request memory req = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + emit Request(req.id, req.callbackAddress, req.callbackFunctionId, req.buf.buf); + } + + function publicRequest( + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory req = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + sendChainlinkRequest(req, _wei); + } + + function publicRequestRunTo( + address _oracle, + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory run = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + sendChainlinkRequestTo(_oracle, run, _wei); + } + + function publicRequestOracleData( + bytes32 _id, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory req = buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature))); + sendOperatorRequest(req, _wei); + } + + function publicRequestOracleDataFrom( + address _oracle, + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory run = buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature))); + sendOperatorRequestTo(_oracle, run, _wei); + } + + function publicCancelRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function publicChainlinkToken() public view returns (address) { + return chainlinkTokenAddress(); + } + + function publicFulfillChainlinkRequest(bytes32 _requestId, bytes32) public { + fulfillRequest(_requestId, bytes32(0)); + } + + function fulfillRequest(bytes32 _requestId, bytes32) public { + validateChainlinkCallback(_requestId); + } + + function publicLINK(uint256 _amount) public { + emit LinkAmount(LINK_DIVISIBILITY.mul(_amount)); + } + + function publicOracleAddress() public view returns (address) { + return chainlinkOracleAddress(); + } + + function publicAddExternalRequest(address _oracle, bytes32 _requestId) public { + addChainlinkExternalRequest(_oracle, _requestId); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ChainlinkTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ChainlinkTestHelper.sol new file mode 100644 index 0000000..6a27e1e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ChainlinkTestHelper.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../Chainlink.sol"; +import "../vendor/CBORChainlink.sol"; +import "../vendor/BufferChainlink.sol"; + +contract ChainlinkTestHelper { + using Chainlink for Chainlink.Request; + using CBORChainlink for BufferChainlink.buffer; + + Chainlink.Request private req; + + event RequestData(bytes payload); + + function closeEvent() public { + emit RequestData(req.buf.buf); + } + + function setBuffer(bytes memory data) public { + Chainlink.Request memory r2 = req; + r2.setBuffer(data); + req = r2; + } + + function add(string memory _key, string memory _value) public { + Chainlink.Request memory r2 = req; + r2.add(_key, _value); + req = r2; + } + + function addBytes(string memory _key, bytes memory _value) public { + Chainlink.Request memory r2 = req; + r2.addBytes(_key, _value); + req = r2; + } + + function addInt(string memory _key, int256 _value) public { + Chainlink.Request memory r2 = req; + r2.addInt(_key, _value); + req = r2; + } + + function addUint(string memory _key, uint256 _value) public { + Chainlink.Request memory r2 = req; + r2.addUint(_key, _value); + req = r2; + } + + // Temporarily have method receive bytes32[] memory until experimental + // string[] memory can be invoked from truffle tests. + function addStringArray(string memory _key, bytes32[] memory _values) public { + string[] memory strings = new string[](_values.length); + for (uint256 i = 0; i < _values.length; i++) { + strings[i] = bytes32ToString(_values[i]); + } + Chainlink.Request memory r2 = req; + r2.addStringArray(_key, strings); + req = r2; + } + + function bytes32ToString(bytes32 x) private pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint256 charCount = 0; + for (uint256 j = 0; j < 32; j++) { + bytes1 char = bytes1(bytes32(uint256(x) * 2**(8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint256 j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ConfirmedOwnerTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ConfirmedOwnerTestHelper.sol new file mode 100644 index 0000000..99a0770 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/ConfirmedOwnerTestHelper.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../ConfirmedOwner.sol"; + +contract ConfirmedOwnerTestHelper is ConfirmedOwner { + event Here(); + + constructor() ConfirmedOwner(msg.sender) {} + + function modifierOnlyOwner() public onlyOwner { + emit Here(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/Consumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/Consumer.sol new file mode 100644 index 0000000..99934df --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/Consumer.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../ChainlinkClient.sol"; + +contract Consumer is ChainlinkClient { + using Chainlink for Chainlink.Request; + + bytes32 internal specId; + bytes32 public currentPrice; + uint256 public currentPriceInt; + + event RequestFulfilled( + bytes32 indexed requestId, // User-defined ID + bytes32 indexed price + ); + + constructor( + address _link, + address _oracle, + bytes32 _specId + ) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + + function setSpecID(bytes32 _specId) public { + specId = _specId; + } + + function requestEthereumPrice(string memory _currency, uint256 _payment) public { + Chainlink.Request memory req = buildOperatorRequest(specId, this.fulfill.selector); + req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY"); + string[] memory path = new string[](1); + path[0] = _currency; + req.addStringArray("path", path); + // version 2 + sendChainlinkRequest(req, _payment); + } + + function requestMultipleParametersWithCustomURLs( + string memory _urlUSD, + string memory _pathUSD, + uint256 _payment + ) public { + Chainlink.Request memory req = buildOperatorRequest(specId, this.fulfillParametersWithCustomURLs.selector); + req.add("urlUSD", _urlUSD); + req.add("pathUSD", _pathUSD); + sendChainlinkRequest(req, _payment); + } + + function cancelRequest( + address _oracle, + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(_oracle); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function withdrawLink() public { + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer"); + } + + function addExternalRequest(address _oracle, bytes32 _requestId) external { + addChainlinkExternalRequest(_oracle, _requestId); + } + + function fulfill(bytes32 _requestId, bytes32 _price) public recordChainlinkFulfillment(_requestId) { + emit RequestFulfilled(_requestId, _price); + currentPrice = _price; + } + + function fulfillParametersWithCustomURLs(bytes32 _requestId, uint256 _price) + public + recordChainlinkFulfillment(_requestId) + { + emit RequestFulfilled(_requestId, bytes32(_price)); + currentPriceInt = _price; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/DerivedPriceFeed.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/DerivedPriceFeed.sol new file mode 100644 index 0000000..43cd72a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/DerivedPriceFeed.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/AggregatorV2V3Interface.sol"; + +/** + * Network: Fantom Testnet + * Base: LINK/USD + * Base Address: 0x6d5689Ad4C1806D1BA0c70Ab95ebe0Da6B204fC5 + * Quote: FTM/USD + * Quote Address: 0xe04676B9A9A2973BCb0D1478b5E1E9098BBB7f3D + * Decimals: 8 + * + * Network: AVAX Testnet + * Base: LINK/USD + * Base Address: 0x34C4c526902d88a3Aa98DB8a9b802603EB1E3470 + * Quote: AVAX/USD + * Quote Address: 0x5498BB86BC934c8D34FDA08E81D444153d0D06aD + * Decimals: 8 + * + * Chainlink Data Feeds can be used in combination to derive denominated price pairs in other currencies. + * If you require a denomination other than what is provided, you can use two data feeds to derive the pair that you need. + * For example, if you needed a LINK / FTM price, you could take the LINK / USD feed and the FTM / USD feed and derive LINK / FTM using division. + * (LINK/USD)/(FTM/USD) = LINK/FTM + */ +contract DerivedPriceFeed is AggregatorV2V3Interface { + uint256 public constant override version = 0; + + uint8 public override decimals; + int256 public override latestAnswer; + uint256 public override latestTimestamp; + uint256 public override latestRound; + + mapping(uint256 => int256) public override getAnswer; + mapping(uint256 => uint256) public override getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + AggregatorV3Interface public base; + AggregatorV3Interface public quote; + + constructor( + address _base, + address _quote, + uint8 _decimals + ) { + require(_decimals > uint8(0) && _decimals <= uint8(18), "Invalid _decimals"); + decimals = _decimals; + base = AggregatorV3Interface(_base); + quote = AggregatorV3Interface(_quote); + } + + function getRoundData(uint80) + external + pure + override + returns ( + uint80, + int256, + uint256, + uint256, + uint80 + ) + { + revert("not implemented - use latestRoundData()"); + } + + function description() external pure override returns (string memory) { + return "DerivedPriceFeed.sol"; + } + + function latestRoundData() + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return (uint80(0), getDerivedPrice(base, quote, decimals), block.timestamp, block.timestamp, uint80(0)); + } + + // https://docs.chain.link/docs/get-the-latest-price/#getting-a-different-price-denomination + function getDerivedPrice( + AggregatorV3Interface _base, + AggregatorV3Interface _quote, + uint8 _decimals + ) internal view returns (int256) { + int256 decimals = int256(10**uint256(_decimals)); + (, int256 basePrice, , , ) = _base.latestRoundData(); + uint8 baseDecimals = _base.decimals(); + basePrice = scalePrice(basePrice, baseDecimals, _decimals); + + (, int256 quotePrice, , , ) = _quote.latestRoundData(); + uint8 quoteDecimals = _quote.decimals(); + quotePrice = scalePrice(quotePrice, quoteDecimals, _decimals); + + return (basePrice * decimals) / quotePrice; + } + + function scalePrice( + int256 _price, + uint8 _priceDecimals, + uint8 _decimals + ) internal pure returns (int256) { + if (_priceDecimals < _decimals) { + return _price * int256(10**uint256(_decimals - _priceDecimals)); + } else if (_priceDecimals > _decimals) { + return _price / int256(10**uint256(_priceDecimals - _decimals)); + } + return _price; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/KeeperCompatibleTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/KeeperCompatibleTestHelper.sol new file mode 100644 index 0000000..34d3b50 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/KeeperCompatibleTestHelper.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../KeeperCompatible.sol"; + +contract KeeperCompatibleTestHelper is KeeperCompatible { + function checkUpkeep(bytes calldata) external override returns (bool, bytes memory) {} + + function performUpkeep(bytes calldata) external override {} + + function testCannotExecute() public view cannotExecute {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockCompoundOracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockCompoundOracle.sol new file mode 100644 index 0000000..0b9ff57 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockCompoundOracle.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/UniswapAnchoredView.sol"; + +contract MockCompoundOracle is UniswapAnchoredView { + struct OracleDetails { + uint256 price; + uint256 decimals; + } + + mapping(string => OracleDetails) s_oracleDetails; + + function price(string memory symbol) external view override returns (uint256) { + return s_oracleDetails[symbol].price; + } + + function setPrice( + string memory symbol, + uint256 newPrice, + uint256 newDecimals + ) public { + OracleDetails memory details = s_oracleDetails[symbol]; + details.price = newPrice; + details.decimals = newDecimals; + s_oracleDetails[symbol] = details; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockV2Aggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockV2Aggregator.sol new file mode 100644 index 0000000..2aeb840 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockV2Aggregator.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/AggregatorInterface.sol"; + +/** + * @title MockV2Aggregator + * @notice Based on the HistoricAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV2Aggregator is AggregatorInterface { + int256 public override latestAnswer; + uint256 public override latestTimestamp; + uint256 public override latestRound; + + mapping(uint256 => int256) public override getAnswer; + mapping(uint256 => uint256) public override getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor(int256 _initialAnswer) public { + updateAnswer(_initialAnswer); + } + + function updateAnswer(int256 _answer) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + } + + function updateRoundData( + uint256 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockV3Aggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockV3Aggregator.sol new file mode 100644 index 0000000..d14a67d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MockV3Aggregator.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/AggregatorV2V3Interface.sol"; + +/** + * @title MockV3Aggregator + * @notice Based on the FluxAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV3Aggregator is AggregatorV2V3Interface { + uint256 public constant override version = 0; + + uint8 public override decimals; + int256 public override latestAnswer; + uint256 public override latestTimestamp; + uint256 public override latestRound; + + mapping(uint256 => int256) public override getAnswer; + mapping(uint256 => uint256) public override getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor(uint8 _decimals, int256 _initialAnswer) { + decimals = _decimals; + updateAnswer(_initialAnswer); + } + + function updateAnswer(int256 _answer) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + getStartedAt[latestRound] = block.timestamp; + } + + function updateRoundData( + uint80 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } + + function getRoundData(uint80 _roundId) + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return (_roundId, getAnswer[_roundId], getStartedAt[_roundId], getTimestamp[_roundId], _roundId); + } + + function latestRoundData() + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + uint80(latestRound), + getAnswer[latestRound], + getStartedAt[latestRound], + getTimestamp[latestRound], + uint80(latestRound) + ); + } + + function description() external pure override returns (string memory) { + return "v0.6/tests/MockV3Aggregator.sol"; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MultiWordConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MultiWordConsumer.sol new file mode 100644 index 0000000..7cadbb2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/MultiWordConsumer.sol @@ -0,0 +1,129 @@ +pragma solidity ^0.7.0; + +import "../ChainlinkClient.sol"; +import "../Chainlink.sol"; + +contract MultiWordConsumer is ChainlinkClient { + using Chainlink for Chainlink.Request; + + bytes32 internal specId; + bytes public currentPrice; + + bytes32 public usd; + bytes32 public eur; + bytes32 public jpy; + + uint256 public usdInt; + uint256 public eurInt; + uint256 public jpyInt; + + event RequestFulfilled( + bytes32 indexed requestId, // User-defined ID + bytes indexed price + ); + + event RequestMultipleFulfilled(bytes32 indexed requestId, bytes32 indexed usd, bytes32 indexed eur, bytes32 jpy); + + event RequestMultipleFulfilledWithCustomURLs( + bytes32 indexed requestId, + uint256 indexed usd, + uint256 indexed eur, + uint256 jpy + ); + + constructor( + address _link, + address _oracle, + bytes32 _specId + ) public { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + specId = _specId; + } + + function setSpecID(bytes32 _specId) public { + specId = _specId; + } + + function requestEthereumPrice(string memory _currency, uint256 _payment) public { + Chainlink.Request memory req = buildOperatorRequest(specId, this.fulfillBytes.selector); + sendOperatorRequest(req, _payment); + } + + function requestMultipleParameters(string memory _currency, uint256 _payment) public { + Chainlink.Request memory req = buildOperatorRequest(specId, this.fulfillMultipleParameters.selector); + sendOperatorRequest(req, _payment); + } + + function requestMultipleParametersWithCustomURLs( + string memory _urlUSD, + string memory _pathUSD, + string memory _urlEUR, + string memory _pathEUR, + string memory _urlJPY, + string memory _pathJPY, + uint256 _payment + ) public { + Chainlink.Request memory req = buildOperatorRequest(specId, this.fulfillMultipleParametersWithCustomURLs.selector); + req.add("urlUSD", _urlUSD); + req.add("pathUSD", _pathUSD); + req.add("urlEUR", _urlEUR); + req.add("pathEUR", _pathEUR); + req.add("urlJPY", _urlJPY); + req.add("pathJPY", _pathJPY); + sendOperatorRequest(req, _payment); + } + + function cancelRequest( + address _oracle, + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + ChainlinkRequestInterface requested = ChainlinkRequestInterface(_oracle); + requested.cancelOracleRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function withdrawLink() public { + LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress()); + require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer"); + } + + function addExternalRequest(address _oracle, bytes32 _requestId) external { + addChainlinkExternalRequest(_oracle, _requestId); + } + + function fulfillMultipleParameters( + bytes32 _requestId, + bytes32 _usd, + bytes32 _eur, + bytes32 _jpy + ) public recordChainlinkFulfillment(_requestId) { + emit RequestMultipleFulfilled(_requestId, _usd, _eur, _jpy); + usd = _usd; + eur = _eur; + jpy = _jpy; + } + + function fulfillMultipleParametersWithCustomURLs( + bytes32 _requestId, + uint256 _usd, + uint256 _eur, + uint256 _jpy + ) public recordChainlinkFulfillment(_requestId) { + emit RequestMultipleFulfilledWithCustomURLs(_requestId, _usd, _eur, _jpy); + usdInt = _usd; + eurInt = _eur; + jpyInt = _jpy; + } + + function fulfillBytes(bytes32 _requestId, bytes memory _price) public recordChainlinkFulfillment(_requestId) { + emit RequestFulfilled(_requestId, _price); + currentPrice = _price; + } + + function publicGetNextRequestCount() external view returns (uint256) { + return getNextRequestCount(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/OperatorForwarderDeployer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/OperatorForwarderDeployer.sol new file mode 100644 index 0000000..e32ae29 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/OperatorForwarderDeployer.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../dev/OperatorForwarder.sol"; +import "../dev/ConfirmedOwner.sol"; + +contract OperatorForwarderDeployer is ConfirmedOwner { + + address private immutable linkAddress; + address[] private s_authorisedSenders; + + event ForwarderDeployed(address indexed forwarder); + + constructor( + address link, + address[] memory authorizedSenders + ) + ConfirmedOwner(msg.sender) + { + linkAddress = link; + s_authorisedSenders = authorizedSenders; + } + + function createForwarder() + external + returns ( + address + ) + { + OperatorForwarder newForwarder = new OperatorForwarder(linkAddress); + address forwarderAddress = address(newForwarder); + emit ForwarderDeployed(forwarderAddress); + return forwarderAddress; + } + + function getAuthorizedSenders() + external + view + returns ( + address[] memory + ) + { + return s_authorisedSenders; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepAutoFunder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepAutoFunder.sol new file mode 100644 index 0000000..9de92d0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepAutoFunder.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../KeeperCompatible.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/KeeperRegistryInterface.sol"; +import "../ConfirmedOwner.sol"; + +contract UpkeepAutoFunder is KeeperCompatible, ConfirmedOwner { + bool public s_isEligible; + bool public s_shouldCancel; + uint256 public s_upkeepId; + uint96 public s_autoFundLink; + LinkTokenInterface public immutable LINK; + KeeperRegistryBaseInterface public immutable s_keeperRegistry; + + constructor(address linkAddress, address registryAddress) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(linkAddress); + s_keeperRegistry = KeeperRegistryBaseInterface(registryAddress); + + s_isEligible = false; + s_shouldCancel = false; + s_upkeepId = 0; + s_autoFundLink = 0; + } + + function setShouldCancel(bool value) external onlyOwner { + s_shouldCancel = value; + } + + function setIsEligible(bool value) external onlyOwner { + s_isEligible = value; + } + + function setAutoFundLink(uint96 value) external onlyOwner { + s_autoFundLink = value; + } + + function setUpkeepId(uint256 value) external onlyOwner { + s_upkeepId = value; + } + + function checkUpkeep(bytes calldata data) + external + override + cannotExecute + returns (bool callable, bytes calldata executedata) + { + return (s_isEligible, data); + } + + function performUpkeep(bytes calldata data) external override { + require(s_isEligible, "Upkeep should be eligible"); + s_isEligible = false; // Allow upkeep only once until it is set again + + // Topup upkeep so it can be called again + LINK.transferAndCall(address(s_keeperRegistry), s_autoFundLink, abi.encode(s_upkeepId)); + + if (s_shouldCancel) { + s_keeperRegistry.cancelUpkeep(s_upkeepId); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepCounter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepCounter.sol new file mode 100644 index 0000000..3c42b58 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepCounter.sol @@ -0,0 +1,57 @@ +pragma solidity ^0.7.6; + +contract UpkeepCounter { + event PerformingUpkeep( + address indexed from, + uint256 initialBlock, + uint256 lastBlock, + uint256 previousBlock, + uint256 counter + ); + + uint256 public testRange; + uint256 public interval; + uint256 public lastBlock; + uint256 public previousPerformBlock; + uint256 public initialBlock; + uint256 public counter; + + constructor(uint256 _testRange, uint256 _interval) { + testRange = _testRange; + interval = _interval; + previousPerformBlock = 0; + lastBlock = block.number; + initialBlock = 0; + counter = 0; + } + + function checkUpkeep(bytes calldata data) external view returns (bool, bytes memory) { + return (eligible(), data); + } + + function performUpkeep(bytes calldata performData) external { + if (initialBlock == 0) { + initialBlock = block.number; + } + lastBlock = block.number; + counter = counter + 1; + performData; + emit PerformingUpkeep(tx.origin, initialBlock, lastBlock, previousPerformBlock, counter); + previousPerformBlock = lastBlock; + } + + function eligible() public view returns (bool) { + if (initialBlock == 0) { + return true; + } + + return (block.number - initialBlock) < testRange && (block.number - lastBlock) >= interval; + } + + function setSpread(uint256 _testRange, uint256 _interval) external { + testRange = _testRange; + interval = _interval; + initialBlock = 0; + counter = 0; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepMock.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepMock.sol new file mode 100644 index 0000000..0ad2015 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepMock.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../KeeperCompatible.sol"; + +contract UpkeepMock is KeeperCompatible { + bool public canCheck; + bool public canPerform; + uint256 public checkGasToBurn; + uint256 public performGasToBurn; + + uint256 constant gasBuffer = 1000; // use all but this amount in gas burn loops + + event UpkeepPerformedWith(bytes upkeepData); + + function setCanCheck(bool value) public { + canCheck = value; + } + + function setCanPerform(bool value) public { + canPerform = value; + } + + function setCheckGasToBurn(uint256 value) public { + require(value > gasBuffer || value == 0, "checkGasToBurn must be 0 (disabled) or greater than buffer"); + checkGasToBurn = value - gasBuffer; + } + + function setPerformGasToBurn(uint256 value) public { + require(value > gasBuffer || value == 0, "performGasToBurn must be 0 (disabled) or greater than buffer"); + performGasToBurn = value - gasBuffer; + } + + function checkUpkeep(bytes calldata data) + external + override + cannotExecute + returns (bool callable, bytes calldata executedata) + { + uint256 startGas = gasleft(); + bool couldCheck = canCheck; + + setCanCheck(false); // test that state modifications don't stick + + while (startGas - gasleft() < checkGasToBurn) {} // burn gas + + return (couldCheck, data); + } + + function performUpkeep(bytes calldata data) external override { + uint256 startGas = gasleft(); + + require(canPerform, "Cannot perform"); + + setCanPerform(false); + + emit UpkeepPerformedWith(data); + + while (startGas - gasleft() < performGasToBurn) {} // burn gas + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepPerformCounterRestrictive.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepPerformCounterRestrictive.sol new file mode 100644 index 0000000..35e2858 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepPerformCounterRestrictive.sol @@ -0,0 +1,85 @@ +pragma solidity 0.7.6; + +contract UpkeepPerformCounterRestrictive { + event PerformingUpkeep(bool eligible, address from, uint256 initialCall, uint256 nextEligible, uint256 blockNumber); + + uint256 public initialCall = 0; + uint256 public nextEligible = 0; + uint256 public testRange; + uint256 public averageEligibilityCadence; + uint256 public checkGasToBurn; + uint256 public performGasToBurn; + mapping(bytes32 => bool) public dummyMap; // used to force storage lookup + + uint256 private count = 0; + + constructor(uint256 _testRange, uint256 _averageEligibilityCadence) { + testRange = _testRange; + averageEligibilityCadence = _averageEligibilityCadence; + } + + function checkUpkeep(bytes calldata data) external view returns (bool, bytes memory) { + uint256 startGas = gasleft(); + uint256 blockNum = block.number - 1; + bool dummy; + // burn gas + while (startGas - gasleft() < checkGasToBurn) { + dummy = dummy && dummyMap[blockhash(blockNum)]; // arbitrary storage reads + blockNum--; + } + return (eligible(), abi.encode(dummy)); + } + + function performUpkeep(bytes calldata) external { + uint256 startGas = gasleft(); + bool eligible = eligible(); + uint256 blockNum = block.number; + emit PerformingUpkeep(eligible, tx.origin, initialCall, nextEligible, blockNum); + require(eligible); + if (initialCall == 0) { + initialCall = blockNum; + } + nextEligible = (blockNum + (rand() % (averageEligibilityCadence * 2))) + 1; + count++; + // burn gas + blockNum--; + while (startGas - gasleft() < performGasToBurn) { + dummyMap[blockhash(blockNum)] = false; // arbitrary storage writes + blockNum--; + } + } + + function setCheckGasToBurn(uint256 value) public { + checkGasToBurn = value; + } + + function setPerformGasToBurn(uint256 value) public { + performGasToBurn = value; + } + + function getCountPerforms() public view returns (uint256) { + return count; + } + + function eligible() internal view returns (bool) { + return initialCall == 0 || (block.number - initialCall < testRange && block.number > nextEligible); + } + + function checkEligible() public view returns (bool) { + return eligible(); + } + + function reset() external { + initialCall = 0; + count = 0; + } + + function setSpread(uint256 _newTestRange, uint256 _newAverageEligibilityCadence) external { + testRange = _newTestRange; + averageEligibilityCadence = _newAverageEligibilityCadence; + } + + function rand() private view returns (uint256) { + return uint256(keccak256(abi.encode(blockhash(block.number - 1), address(this)))); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepReverter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepReverter.sol new file mode 100644 index 0000000..c39cbf7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/UpkeepReverter.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../KeeperCompatible.sol"; + +contract UpkeepReverter is KeeperCompatible { + function checkUpkeep(bytes calldata data) + public + view + override + cannotExecute + returns (bool callable, bytes calldata executedata) + { + require(false, "!working"); + return (true, data); + } + + function performUpkeep(bytes calldata) external pure override { + require(false, "!working"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/VRFCoordinatorMock.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/VRFCoordinatorMock.sol new file mode 100644 index 0000000..a46c9a7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/tests/VRFCoordinatorMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../VRFConsumerBase.sol"; + +contract VRFCoordinatorMock { + LinkTokenInterface public LINK; + + event RandomnessRequest(address indexed sender, bytes32 indexed keyHash, uint256 indexed seed); + + constructor(address linkAddress) public { + LINK = LinkTokenInterface(linkAddress); + } + + function onTokenTransfer( + address sender, + uint256 fee, + bytes memory _data + ) public onlyLINK { + (bytes32 keyHash, uint256 seed) = abi.decode(_data, (bytes32, uint256)); + emit RandomnessRequest(sender, keyHash, seed); + } + + function callBackWithRandomness( + bytes32 requestId, + uint256 randomness, + address consumerContract + ) public { + VRFConsumerBase v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomness.selector, requestId, randomness); + uint256 b = 206000; + require(gasleft() >= b, "not enough gas for consumer"); + (bool success, ) = consumerContract.call(resp); + } + + modifier onlyLINK() { + require(msg.sender == address(LINK), "Must use LINK token"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Address.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Address.sol new file mode 100644 index 0000000..eec7c36 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Address.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT +// From https://github.com/OpenZeppelin/openzeppelin-contracts v3.4.0(fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6) + +pragma solidity >=0.6.2 <0.8.0; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { + size := extcodesize(account) + } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{value: value}(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) private pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/BufferChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/BufferChainlink.sol new file mode 100644 index 0000000..7d20569 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/BufferChainlink.sol @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +/** + * @dev A library for working with mutable byte buffers in Solidity. + * + * Byte buffers are mutable and expandable, and provide a variety of primitives + * for writing to them. At any time you can fetch a bytes object containing the + * current contents of the buffer. The bytes object should not be stored between + * operations, as it may change due to resizing of the buffer. + */ +library BufferChainlink { + /** + * @dev Represents a mutable buffer. Buffers have a current value (buf) and + * a capacity. The capacity may be longer than the current value, in + * which case it can be extended without the need to allocate more memory. + */ + struct buffer { + bytes buf; + uint256 capacity; + } + + /** + * @dev Initializes a buffer with an initial capacity. + * @param buf The buffer to initialize. + * @param capacity The number of bytes of space to allocate the buffer. + * @return The buffer, for chaining. + */ + function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + // Allocate space for the buffer data + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + mstore(0x40, add(32, add(ptr, capacity))) + } + return buf; + } + + /** + * @dev Initializes a new buffer from an existing bytes object. + * Changes to the buffer may mutate the original value. + * @param b The bytes object to initialize the buffer with. + * @return A new buffer. + */ + function fromBytes(bytes memory b) internal pure returns (buffer memory) { + buffer memory buf; + buf.buf = b; + buf.capacity = b.length; + return buf; + } + + function resize(buffer memory buf, uint256 capacity) private pure { + bytes memory oldbuf = buf.buf; + init(buf, capacity); + append(buf, oldbuf); + } + + function max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) { + return a; + } + return b; + } + + /** + * @dev Sets buffer length to 0. + * @param buf The buffer to truncate. + * @return The original buffer, for chaining.. + */ + function truncate(buffer memory buf) internal pure returns (buffer memory) { + assembly { + let bufptr := mload(buf) + mstore(bufptr, 0) + } + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function write( + buffer memory buf, + uint256 off, + bytes memory data, + uint256 len + ) internal pure returns (buffer memory) { + require(len <= data.length); + + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint256 dest; + uint256 src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(data, 32) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint256 mask = 256**(32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + + return buf; + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function append( + buffer memory buf, + bytes memory data, + uint256 len + ) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, len); + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, data.length); + } + + /** + * @dev Writes a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write the byte at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeUint8( + buffer memory buf, + uint256 off, + uint8 data + ) internal pure returns (buffer memory) { + if (off >= buf.capacity) { + resize(buf, buf.capacity * 2); + } + + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Address = buffer address + sizeof(buffer length) + off + let dest := add(add(bufptr, off), 32) + mstore8(dest, data) + // Update buffer length if we extended it + if eq(off, buflen) { + mstore(bufptr, add(buflen, 1)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) { + return writeUint8(buf, buf.buf.length, data); + } + + /** + * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (left-aligned). + * @return The original buffer, for chaining. + */ + function write( + buffer memory buf, + uint256 off, + bytes32 data, + uint256 len + ) private pure returns (buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint256 mask = 256**len - 1; + // Right-align data + data = data >> (8 * (32 - len)); + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + sizeof(buffer length) + off + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeBytes20( + buffer memory buf, + uint256 off, + bytes20 data + ) internal pure returns (buffer memory) { + return write(buf, off, bytes32(data), 20); + } + + /** + * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chhaining. + */ + function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, bytes32(data), 20); + } + + /** + * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, 32); + } + + /** + * @dev Writes an integer to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (right-aligned). + * @return The original buffer, for chaining. + */ + function writeInt( + buffer memory buf, + uint256 off, + uint256 data, + uint256 len + ) private pure returns (buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint256 mask = 256**len - 1; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + off + sizeof(buffer length) + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer. + */ + function appendInt( + buffer memory buf, + uint256 data, + uint256 len + ) internal pure returns (buffer memory) { + return writeInt(buf, buf.buf.length, data, len); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/CBORChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/CBORChainlink.sol new file mode 100644 index 0000000..5ee0cec --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/CBORChainlink.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.19; + +import {BufferChainlink} from "./BufferChainlink.sol"; + +library CBORChainlink { + using BufferChainlink for BufferChainlink.buffer; + + uint8 private constant MAJOR_TYPE_INT = 0; + uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 private constant MAJOR_TYPE_BYTES = 2; + uint8 private constant MAJOR_TYPE_STRING = 3; + uint8 private constant MAJOR_TYPE_ARRAY = 4; + uint8 private constant MAJOR_TYPE_MAP = 5; + uint8 private constant MAJOR_TYPE_TAG = 6; + uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; + + uint8 private constant TAG_TYPE_BIGNUM = 2; + uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; + + function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure { + if(value <= 23) { + buf.appendUint8(uint8((major << 5) | value)); + } else if (value <= 0xFF) { + buf.appendUint8(uint8((major << 5) | 24)); + buf.appendInt(value, 1); + } else if (value <= 0xFFFF) { + buf.appendUint8(uint8((major << 5) | 25)); + buf.appendInt(value, 2); + } else if (value <= 0xFFFFFFFF) { + buf.appendUint8(uint8((major << 5) | 26)); + buf.appendInt(value, 4); + } else { + buf.appendUint8(uint8((major << 5) | 27)); + buf.appendInt(value, 8); + } + } + + function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure { + buf.appendUint8(uint8((major << 5) | 31)); + } + + function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure { + if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, value); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } + } + + function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure { + if(value < -0x10000000000000000) { + encodeSignedBigNum(buf, value); + } else if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, uint(value)); + } else if(value >= 0) { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value))); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value))); + } + } + + function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); + buf.append(value); + } + + function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); + encodeBytes(buf, abi.encode(value)); + } + + function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); + encodeBytes(buf, abi.encode(uint256(-1 - input))); + } + + function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); + buf.append(bytes(value)); + } + + function startArray(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); + } + + function startMap(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); + } + + function endSequence(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Context.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Context.sol new file mode 100644 index 0000000..aa7b856 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Context.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// github.com/OpenZeppelin/openzeppelin-contracts@fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6 + +pragma solidity ^0.7.0; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; + // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/ENSResolver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/ENSResolver.sol new file mode 100644 index 0000000..d5cbc67 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/ENSResolver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +abstract contract ENSResolver { + function addr(bytes32 node) public view virtual returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Pausable.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Pausable.sol new file mode 100644 index 0000000..63ccdd6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/Pausable.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// github.com/OpenZeppelin/openzeppelin-contracts@fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6 + +pragma solidity ^0.7.0; + +import "./Context.sol"; + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + require(!paused(), "Pausable: paused"); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + require(paused(), "Pausable: not paused"); + _; + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/ReentrancyGuard.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/ReentrancyGuard.sol new file mode 100644 index 0000000..aaaee17 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/ReentrancyGuard.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// github.com/OpenZeppelin/openzeppelin-contracts@fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6 + +pragma solidity ^0.7.0; + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor() { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and make it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SafeMath96.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SafeMath96.sol new file mode 100644 index 0000000..b518499 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SafeMath96.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.7.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * This library is a version of Open Zeppelin's SafeMath, modified to support + * unsigned 96 bit integers. + */ +library SafeMath96 { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint96 a, uint96 b) internal pure returns (uint96) { + uint96 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint96 a, uint96 b) internal pure returns (uint96) { + require(b <= a, "SafeMath: subtraction overflow"); + uint96 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint96 a, uint96 b) internal pure returns (uint96) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint96 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint96 a, uint96 b) internal pure returns (uint96) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint96 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint96 a, uint96 b) internal pure returns (uint96) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SafeMathChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SafeMathChainlink.sol new file mode 100644 index 0000000..1a95b1a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SafeMathChainlink.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMathChainlink { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SafeMath: subtraction overflow"); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, "SafeMath: division by zero"); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, "SafeMath: modulo by zero"); + return a % b; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SignedSafeMath.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SignedSafeMath.sol new file mode 100644 index 0000000..61658cd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.7/vendor/SignedSafeMath.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.7.0; + +/** + * @title SignedSafeMath + * @dev Signed math operations with safety checks that revert on error. + */ +library SignedSafeMath { + int256 private constant _INT256_MIN = -2**255; + + /** + * @dev Returns the multiplication of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); + + int256 c = a * b; + require(c / a == b, "SignedSafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two signed integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + require(b != 0, "SignedSafeMath: division by zero"); + require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); + + int256 c = a / b; + + return c; + } + + /** + * @dev Returns the subtraction of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + int256 c = a - b; + require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); + + return c; + } + + /** + * @dev Returns the addition of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + int256 c = a + b; + require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); + + return c; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/AutomationBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/AutomationBase.sol new file mode 100644 index 0000000..d91780a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/AutomationBase.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract AutomationBase { + error OnlySimulatedBackend(); + + /** + * @notice method that allows it to be simulated via eth_call by checking that + * the sender is the zero address. + */ + function preventExecution() internal view { + if (tx.origin != address(0)) { + revert OnlySimulatedBackend(); + } + } + + /** + * @notice modifier that allows it to be simulated via eth_call by checking + * that the sender is the zero address. + */ + modifier cannotExecute() { + preventExecution(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/AutomationCompatible.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/AutomationCompatible.sol new file mode 100644 index 0000000..5634956 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/AutomationCompatible.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./AutomationBase.sol"; +import "./interfaces/AutomationCompatibleInterface.sol"; + +abstract contract AutomationCompatible is AutomationBase, AutomationCompatibleInterface {} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/BatchBlockhashStore.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/BatchBlockhashStore.sol new file mode 100644 index 0000000..ca0c29c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/BatchBlockhashStore.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +/** + * @title BatchBlockhashStore + * @notice The BatchBlockhashStore contract acts as a proxy to write many blockhashes to the + * provided BlockhashStore contract efficiently in a single transaction. This results + * in plenty of gas savings and higher throughput of blockhash storage, which is desirable + * in times of high network congestion. + */ +contract BatchBlockhashStore { + BlockhashStore public immutable BHS; + + constructor(address blockhashStoreAddr) { + BHS = BlockhashStore(blockhashStoreAddr); + } + + /** + * @notice stores blockhashes of the given block numbers in the configured blockhash store, assuming + * they are availble though the blockhash() instruction. + * @param blockNumbers the block numbers to store the blockhashes of. Must be available via the + * blockhash() instruction, otherwise this function call will revert. + */ + function store(uint256[] memory blockNumbers) public { + for (uint256 i = 0; i < blockNumbers.length; i++) { + // skip the block if it's not storeable, the caller will have to check + // after the transaction is mined to see if the blockhash was truly stored. + if (!storeableBlock(blockNumbers[i])) { + continue; + } + BHS.store(blockNumbers[i]); + } + } + + /** + * @notice stores blockhashes after verifying blockheader of child/subsequent block + * @param blockNumbers the block numbers whose blockhashes should be stored, in decreasing order + * @param headers the rlp-encoded block headers of blockNumbers[i] + 1. + */ + function storeVerifyHeader(uint256[] memory blockNumbers, bytes[] memory headers) public { + require(blockNumbers.length == headers.length, "input array arg lengths mismatch"); + for (uint256 i = 0; i < blockNumbers.length; i++) { + BHS.storeVerifyHeader(blockNumbers[i], headers[i]); + } + } + + /** + * @notice retrieves blockhashes of all the given block numbers from the blockhash store, if available. + * @param blockNumbers array of block numbers to fetch blockhashes for + * @return blockhashes array of block hashes corresponding to each block number provided in the `blockNumbers` + * param. If the blockhash is not found, 0x0 is returned instead of the real blockhash, indicating + * that it is not in the blockhash store. + */ + function getBlockhashes(uint256[] memory blockNumbers) external view returns (bytes32[] memory) { + bytes32[] memory blockHashes = new bytes32[](blockNumbers.length); + for (uint256 i = 0; i < blockNumbers.length; i++) { + try BHS.getBlockhash(blockNumbers[i]) returns (bytes32 bh) { + blockHashes[i] = bh; + } catch Error( + string memory /* reason */ + ) { + blockHashes[i] = 0x0; + } + } + return blockHashes; + } + + /** + * @notice returns true if and only if the given block number's blockhash can be retrieved + * using the blockhash() instruction. + * @param blockNumber the block number to check if it's storeable with blockhash() + */ + function storeableBlock(uint256 blockNumber) private view returns (bool) { + // handle edge case on simulated chains which possibly have < 256 blocks total. + return block.number <= 256 ? true : blockNumber >= (block.number - 256); + } +} + +interface BlockhashStore { + function storeVerifyHeader(uint256 n, bytes memory header) external; + + function store(uint256 n) external; + + function getBlockhash(uint256 n) external view returns (bytes32); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/BatchVRFCoordinatorV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/BatchVRFCoordinatorV2.sol new file mode 100644 index 0000000..98de0d0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/BatchVRFCoordinatorV2.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "./VRFTypes.sol"; + +/** + * @title BatchVRFCoordinatorV2 + * @notice The BatchVRFCoordinatorV2 contract acts as a proxy to write many random responses to the + * provided VRFCoordinatorV2 contract efficiently in a single transaction. + */ +contract BatchVRFCoordinatorV2 { + VRFCoordinatorV2 public immutable COORDINATOR; + + event ErrorReturned(uint256 indexed requestId, string reason); + event RawErrorReturned(uint256 indexed requestId, bytes lowLevelData); + + constructor(address coordinatorAddr) { + COORDINATOR = VRFCoordinatorV2(coordinatorAddr); + } + + /** + * @notice fulfills multiple randomness requests with the provided proofs and commitments. + * @param proofs the randomness proofs generated by the VRF provider. + * @param rcs the request commitments corresponding to the randomness proofs. + */ + function fulfillRandomWords(VRFTypes.Proof[] memory proofs, VRFTypes.RequestCommitment[] memory rcs) external { + require(proofs.length == rcs.length, "input array arg lengths mismatch"); + for (uint256 i = 0; i < proofs.length; i++) { + try COORDINATOR.fulfillRandomWords(proofs[i], rcs[i]) returns ( + uint96 /* payment */ + ) { + continue; + } catch Error(string memory reason) { + uint256 requestId = getRequestIdFromProof(proofs[i]); + emit ErrorReturned(requestId, reason); + } catch (bytes memory lowLevelData) { + uint256 requestId = getRequestIdFromProof(proofs[i]); + emit RawErrorReturned(requestId, lowLevelData); + } + } + } + + /** + * @notice Returns the proving key hash associated with this public key. + * @param publicKey the key to return the hash of. + */ + function hashOfKey(uint256[2] memory publicKey) internal pure returns (bytes32) { + return keccak256(abi.encode(publicKey)); + } + + /** + * @notice Returns the request ID of the request associated with the given proof. + * @param proof the VRF proof provided by the VRF oracle. + */ + function getRequestIdFromProof(VRFTypes.Proof memory proof) internal pure returns (uint256) { + bytes32 keyHash = hashOfKey(proof.pk); + return uint256(keccak256(abi.encode(keyHash, proof.seed))); + } +} + +interface VRFCoordinatorV2 { + function fulfillRandomWords(VRFTypes.Proof memory proof, VRFTypes.RequestCommitment memory rc) + external + returns (uint96); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/Chainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/Chainlink.sol new file mode 100644 index 0000000..b61227d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/Chainlink.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {CBORChainlink} from "./vendor/CBORChainlink.sol"; +import {BufferChainlink} from "./vendor/BufferChainlink.sol"; + +/** + * @title Library for common Chainlink functions + * @dev Uses imported CBOR library for encoding to buffer + */ +library Chainlink { + uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase + + using CBORChainlink for BufferChainlink.buffer; + + struct Request { + bytes32 id; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + BufferChainlink.buffer buf; + } + + /** + * @notice Initializes a Chainlink request + * @dev Sets the ID, callback address, and callback function signature on the request + * @param self The uninitialized request + * @param jobId The Job Specification ID + * @param callbackAddr The callback address + * @param callbackFunc The callback function signature + * @return The initialized request + */ + function initialize( + Request memory self, + bytes32 jobId, + address callbackAddr, + bytes4 callbackFunc + ) internal pure returns (Chainlink.Request memory) { + BufferChainlink.init(self.buf, defaultBufferSize); + self.id = jobId; + self.callbackAddress = callbackAddr; + self.callbackFunctionId = callbackFunc; + return self; + } + + /** + * @notice Sets the data for the buffer without encoding CBOR on-chain + * @dev CBOR can be closed with curly-brackets {} or they can be left off + * @param self The initialized request + * @param data The CBOR data + */ + function setBuffer(Request memory self, bytes memory data) internal pure { + BufferChainlink.init(self.buf, data.length); + BufferChainlink.append(self.buf, data); + } + + /** + * @notice Adds a string value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The string value to add + */ + function add( + Request memory self, + string memory key, + string memory value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeString(value); + } + + /** + * @notice Adds a bytes value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The bytes value to add + */ + function addBytes( + Request memory self, + string memory key, + bytes memory value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeBytes(value); + } + + /** + * @notice Adds a int256 value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The int256 value to add + */ + function addInt( + Request memory self, + string memory key, + int256 value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeInt(value); + } + + /** + * @notice Adds a uint256 value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The uint256 value to add + */ + function addUint( + Request memory self, + string memory key, + uint256 value + ) internal pure { + self.buf.encodeString(key); + self.buf.encodeUInt(value); + } + + /** + * @notice Adds an array of strings to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param values The array of string values to add + */ + function addStringArray( + Request memory self, + string memory key, + string[] memory values + ) internal pure { + self.buf.encodeString(key); + self.buf.startArray(); + for (uint256 i = 0; i < values.length; i++) { + self.buf.encodeString(values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/ChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ChainlinkClient.sol new file mode 100644 index 0000000..8fe4beb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ChainlinkClient.sol @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Chainlink.sol"; +import "./interfaces/ENSInterface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/ChainlinkRequestInterface.sol"; +import "./interfaces/OperatorInterface.sol"; +import "./interfaces/PointerInterface.sol"; +import {ENSResolver as ENSResolver_Chainlink} from "./vendor/ENSResolver.sol"; + +/** + * @title The ChainlinkClient contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network + */ +abstract contract ChainlinkClient { + using Chainlink for Chainlink.Request; + + uint256 internal constant LINK_DIVISIBILITY = 10**18; + uint256 private constant AMOUNT_OVERRIDE = 0; + address private constant SENDER_OVERRIDE = address(0); + uint256 private constant ORACLE_ARGS_VERSION = 1; + uint256 private constant OPERATOR_ARGS_VERSION = 2; + bytes32 private constant ENS_TOKEN_SUBNAME = keccak256("link"); + bytes32 private constant ENS_ORACLE_SUBNAME = keccak256("oracle"); + address private constant LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; + + ENSInterface private s_ens; + bytes32 private s_ensNode; + LinkTokenInterface private s_link; + OperatorInterface private s_oracle; + uint256 private s_requestCount = 1; + mapping(bytes32 => address) private s_pendingRequests; + + event ChainlinkRequested(bytes32 indexed id); + event ChainlinkFulfilled(bytes32 indexed id); + event ChainlinkCancelled(bytes32 indexed id); + + /** + * @notice Creates a request that can hold additional parameters + * @param specId The Job Specification ID that the request will be created for + * @param callbackAddr address to operate the callback on + * @param callbackFunctionSignature function signature to use for the callback + * @return A Chainlink Request struct in memory + */ + function buildChainlinkRequest( + bytes32 specId, + address callbackAddr, + bytes4 callbackFunctionSignature + ) internal pure returns (Chainlink.Request memory) { + Chainlink.Request memory req; + return req.initialize(specId, callbackAddr, callbackFunctionSignature); + } + + /** + * @notice Creates a request that can hold additional parameters + * @param specId The Job Specification ID that the request will be created for + * @param callbackFunctionSignature function signature to use for the callback + * @return A Chainlink Request struct in memory + */ + function buildOperatorRequest(bytes32 specId, bytes4 callbackFunctionSignature) + internal + view + returns (Chainlink.Request memory) + { + Chainlink.Request memory req; + return req.initialize(specId, address(this), callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `chainlinkRequestTo` with the stored oracle address + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) { + return sendChainlinkRequestTo(address(s_oracle), req, payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequestTo( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment + ) internal returns (bytes32 requestId) { + uint256 nonce = s_requestCount; + s_requestCount = nonce + 1; + bytes memory encodedRequest = abi.encodeWithSelector( + ChainlinkRequestInterface.oracleRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + req.id, + address(this), + req.callbackFunctionId, + nonce, + ORACLE_ARGS_VERSION, + req.buf.buf + ); + return _rawRequest(oracleAddress, nonce, payment, encodedRequest); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev This function supports multi-word response + * @dev Calls `sendOperatorRequestTo` with the stored oracle address + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendOperatorRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) { + return sendOperatorRequestTo(address(s_oracle), req, payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev This function supports multi-word response + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendOperatorRequestTo( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment + ) internal returns (bytes32 requestId) { + uint256 nonce = s_requestCount; + s_requestCount = nonce + 1; + bytes memory encodedRequest = abi.encodeWithSelector( + OperatorInterface.operatorRequest.selector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + req.id, + req.callbackFunctionId, + nonce, + OPERATOR_ARGS_VERSION, + req.buf.buf + ); + return _rawRequest(oracleAddress, nonce, payment, encodedRequest); + } + + /** + * @notice Make a request to an oracle + * @param oracleAddress The address of the oracle for the request + * @param nonce used to generate the request ID + * @param payment The amount of LINK to send for the request + * @param encodedRequest data encoded for request type specific format + * @return requestId The request ID + */ + function _rawRequest( + address oracleAddress, + uint256 nonce, + uint256 payment, + bytes memory encodedRequest + ) private returns (bytes32 requestId) { + requestId = keccak256(abi.encodePacked(this, nonce)); + s_pendingRequests[requestId] = oracleAddress; + emit ChainlinkRequested(requestId); + require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle"); + } + + /** + * @notice Allows a request to be cancelled if it has not been fulfilled + * @dev Requires keeping track of the expiration value emitted from the oracle contract. + * Deletes the request from the `pendingRequests` mapping. + * Emits ChainlinkCancelled event. + * @param requestId The request ID + * @param payment The amount of LINK sent for the request + * @param callbackFunc The callback function specified for the request + * @param expiration The time of the expiration for the request + */ + function cancelChainlinkRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunc, + uint256 expiration + ) internal { + OperatorInterface requested = OperatorInterface(s_pendingRequests[requestId]); + delete s_pendingRequests[requestId]; + emit ChainlinkCancelled(requestId); + requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration); + } + + /** + * @notice the next request count to be used in generating a nonce + * @dev starts at 1 in order to ensure consistent gas cost + * @return returns the next request count to be used in a nonce + */ + function getNextRequestCount() internal view returns (uint256) { + return s_requestCount; + } + + /** + * @notice Sets the stored oracle address + * @param oracleAddress The address of the oracle contract + */ + function setChainlinkOracle(address oracleAddress) internal { + s_oracle = OperatorInterface(oracleAddress); + } + + /** + * @notice Sets the LINK token address + * @param linkAddress The address of the LINK token contract + */ + function setChainlinkToken(address linkAddress) internal { + s_link = LinkTokenInterface(linkAddress); + } + + /** + * @notice Sets the Chainlink token address for the public + * network as given by the Pointer contract + */ + function setPublicChainlinkToken() internal { + setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkTokenAddress() internal view returns (address) { + return address(s_link); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function chainlinkOracleAddress() internal view returns (address) { + return address(s_oracle); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param oracleAddress The address of the oracle contract that will fulfill the request + * @param requestId The request ID used for the response + */ + function addChainlinkExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) { + s_pendingRequests[requestId] = oracleAddress; + } + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param ensAddress The address of the ENS contract + * @param node The ENS node hash + */ + function useChainlinkWithENS(address ensAddress, bytes32 node) internal { + s_ens = ENSInterface(ensAddress); + s_ensNode = node; + bytes32 linkSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_TOKEN_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(linkSubnode)); + setChainlinkToken(resolver.addr(linkSubnode)); + updateChainlinkOracleWithENS(); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously + */ + function updateChainlinkOracleWithENS() internal { + bytes32 oracleSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_ORACLE_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(oracleSubnode)); + setChainlinkOracle(resolver.addr(oracleSubnode)); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param requestId The request ID for fulfillment + */ + function validateChainlinkCallback(bytes32 requestId) + internal + recordChainlinkFulfillment(requestId) + // solhint-disable-next-line no-empty-blocks + { + + } + + /** + * @dev Reverts if the sender is not the oracle of the request. + * Emits ChainlinkFulfilled event. + * @param requestId The request ID for fulfillment + */ + modifier recordChainlinkFulfillment(bytes32 requestId) { + require(msg.sender == s_pendingRequests[requestId], "Source must be the oracle of the request"); + delete s_pendingRequests[requestId]; + emit ChainlinkFulfilled(requestId); + _; + } + + /** + * @dev Reverts if the request is already pending + * @param requestId The request ID for fulfillment + */ + modifier notPendingRequest(bytes32 requestId) { + require(s_pendingRequests[requestId] == address(0), "Request is already pending"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/ConfirmedOwner.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ConfirmedOwner.sol new file mode 100644 index 0000000..a33cff1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ConfirmedOwner.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./ConfirmedOwnerWithProposal.sol"; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwner is ConfirmedOwnerWithProposal { + constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/ConfirmedOwnerWithProposal.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ConfirmedOwnerWithProposal.sol new file mode 100644 index 0000000..65071d1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ConfirmedOwnerWithProposal.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./interfaces/OwnableInterface.sol"; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwnerWithProposal is OwnableInterface { + address private s_owner; + address private s_pendingOwner; + + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + + constructor(address newOwner, address pendingOwner) { + require(newOwner != address(0), "Cannot set owner to zero"); + + s_owner = newOwner; + if (pendingOwner != address(0)) { + _transferOwnership(pendingOwner); + } + } + + /** + * @notice Allows an owner to begin transferring ownership to a new address, + * pending. + */ + function transferOwnership(address to) public override onlyOwner { + _transferOwnership(to); + } + + /** + * @notice Allows an ownership transfer to be completed by the recipient. + */ + function acceptOwnership() external override { + require(msg.sender == s_pendingOwner, "Must be proposed owner"); + + address oldOwner = s_owner; + s_owner = msg.sender; + s_pendingOwner = address(0); + + emit OwnershipTransferred(oldOwner, msg.sender); + } + + /** + * @notice Get the current owner + */ + function owner() public view override returns (address) { + return s_owner; + } + + /** + * @notice validate, transfer ownership, and emit relevant events + */ + function _transferOwnership(address to) private { + require(to != msg.sender, "Cannot transfer to self"); + + s_pendingOwner = to; + + emit OwnershipTransferRequested(s_owner, to); + } + + /** + * @notice validate access + */ + function _validateOwnership() internal view { + require(msg.sender == s_owner, "Only callable by owner"); + } + + /** + * @notice Reverts if called by anyone other than the contract owner. + */ + modifier onlyOwner() { + _validateOwnership(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/Denominations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/Denominations.sol new file mode 100644 index 0000000..6e9aa77 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/Denominations.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +library Denominations { + address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 + address public constant USD = address(840); + address public constant GBP = address(826); + address public constant EUR = address(978); + address public constant JPY = address(392); + address public constant KRW = address(410); + address public constant CNY = address(156); + address public constant AUD = address(36); + address public constant CAD = address(124); + address public constant CHF = address(756); + address public constant ARS = address(32); + address public constant PHP = address(608); + address public constant NZD = address(554); + address public constant SGD = address(702); + address public constant NGN = address(566); + address public constant ZAR = address(710); + address public constant RUB = address(643); + address public constant INR = address(356); + address public constant BRL = address(986); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/Flags.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/Flags.sol new file mode 100644 index 0000000..1aa62fc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/Flags.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./SimpleReadAccessController.sol"; +import "./interfaces/AccessControllerInterface.sol"; +import "./interfaces/FlagsInterface.sol"; + +/** + * @title The Flags contract + * @notice Allows flags to signal to any reader on the access control list. + * The owner can set flags, or designate other addresses to set flags. The + * owner must turn the flags off, other setters cannot. An expected pattern is + * to allow addresses to raise flags on themselves, so if you are subscribing to + * FlagOn events you should filter for addresses you care about. + */ +contract Flags is FlagsInterface, SimpleReadAccessController { + AccessControllerInterface public raisingAccessController; + + mapping(address => bool) private flags; + + event FlagRaised(address indexed subject); + event FlagLowered(address indexed subject); + event RaisingAccessControllerUpdated(address indexed previous, address indexed current); + + /** + * @param racAddress address for the raising access controller. + */ + constructor(address racAddress) { + setRaisingAccessController(racAddress); + } + + /** + * @notice read the warning flag status of a contract address. + * @param subject The contract address being checked for a flag. + * @return A true value indicates that a flag was raised and a + * false value indicates that no flag was raised. + */ + function getFlag(address subject) external view override checkAccess returns (bool) { + return flags[subject]; + } + + /** + * @notice read the warning flag status of a contract address. + * @param subjects An array of addresses being checked for a flag. + * @return An array of bools where a true value for any flag indicates that + * a flag was raised and a false value indicates that no flag was raised. + */ + function getFlags(address[] calldata subjects) external view override checkAccess returns (bool[] memory) { + bool[] memory responses = new bool[](subjects.length); + for (uint256 i = 0; i < subjects.length; i++) { + responses[i] = flags[subjects[i]]; + } + return responses; + } + + /** + * @notice enable the warning flag for an address. + * Access is controlled by raisingAccessController, except for owner + * who always has access. + * @param subject The contract address whose flag is being raised + */ + function raiseFlag(address subject) external override { + require(allowedToRaiseFlags(), "Not allowed to raise flags"); + + tryToRaiseFlag(subject); + } + + /** + * @notice enable the warning flags for multiple addresses. + * Access is controlled by raisingAccessController, except for owner + * who always has access. + * @param subjects List of the contract addresses whose flag is being raised + */ + function raiseFlags(address[] calldata subjects) external override { + require(allowedToRaiseFlags(), "Not allowed to raise flags"); + + for (uint256 i = 0; i < subjects.length; i++) { + tryToRaiseFlag(subjects[i]); + } + } + + /** + * @notice allows owner to disable the warning flags for multiple addresses. + * @param subjects List of the contract addresses whose flag is being lowered + */ + function lowerFlags(address[] calldata subjects) external override onlyOwner { + for (uint256 i = 0; i < subjects.length; i++) { + address subject = subjects[i]; + + if (flags[subject]) { + flags[subject] = false; + emit FlagLowered(subject); + } + } + } + + /** + * @notice allows owner to change the access controller for raising flags. + * @param racAddress new address for the raising access controller. + */ + function setRaisingAccessController(address racAddress) public override onlyOwner { + address previous = address(raisingAccessController); + + if (previous != racAddress) { + raisingAccessController = AccessControllerInterface(racAddress); + + emit RaisingAccessControllerUpdated(previous, racAddress); + } + } + + // PRIVATE + + function allowedToRaiseFlags() private view returns (bool) { + return msg.sender == owner() || raisingAccessController.hasAccess(msg.sender, msg.data); + } + + function tryToRaiseFlag(address subject) private { + if (!flags[subject]) { + flags[subject] = true; + emit FlagRaised(subject); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperBase.sol new file mode 100644 index 0000000..448d4de --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperBase.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +/** + * @notice This is a deprecated interface. Please use AutomationBase directly. + */ +pragma solidity ^0.8.0; +import {AutomationBase as KeeperBase} from "./AutomationBase.sol"; diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperCompatible.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperCompatible.sol new file mode 100644 index 0000000..f5263ed --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperCompatible.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +/** + * @notice This is a deprecated interface. Please use AutomationCompatible directly. + */ +pragma solidity ^0.8.0; +import {AutomationCompatible as KeeperCompatible} from "./AutomationCompatible.sol"; +import {AutomationBase as KeeperBase} from "./AutomationBase.sol"; +import {AutomationCompatibleInterface as KeeperCompatibleInterface} from "./interfaces/AutomationCompatibleInterface.sol"; diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistrar.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistrar.sol new file mode 100644 index 0000000..35b6ea0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistrar.sol @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperRegistryInterface1_2.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./ConfirmedOwner.sol"; +import "./interfaces/ERC677ReceiverInterface.sol"; + +/** + * @notice Contract to accept requests for upkeep registrations + * @dev There are 2 registration workflows in this contract + * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually + * calls `approve` to register upkeep and emit events to inform UI and others interested. + * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on + * keeper registry and then emits approved event to finish the flow automatically without manual intervention. + * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not. + * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations. + */ +contract KeeperRegistrar is TypeAndVersionInterface, ConfirmedOwner, ERC677ReceiverInterface { + /** + * DISABLED: No auto approvals, all new upkeeps should be approved manually. + * ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest. + * ENABLED_ALL: Auto approvals for all new upkeeps subject to max allowed. + */ + enum AutoApproveType { + DISABLED, + ENABLED_SENDER_ALLOWLIST, + ENABLED_ALL + } + + bytes4 private constant REGISTER_REQUEST_SELECTOR = this.register.selector; + + mapping(bytes32 => PendingRequest) private s_pendingRequests; + + LinkTokenInterface public immutable LINK; + + /** + * @notice versions: + * - KeeperRegistrar 1.1.0: Add functionality for sender allowlist in auto approve + * : Remove rate limit and add max allowed for auto approve + * - KeeperRegistrar 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistrar 1.1.0"; + + struct Config { + AutoApproveType autoApproveConfigType; + uint32 autoApproveMaxAllowed; + uint32 approvedCount; + KeeperRegistryBaseInterface keeperRegistry; + uint96 minLINKJuels; + } + + struct PendingRequest { + address admin; + uint96 balance; + } + + Config private s_config; + // Only applicable if s_config.configType is ENABLED_SENDER_ALLOWLIST + mapping(address => bool) private s_autoApproveAllowedSenders; + + event RegistrationRequested( + bytes32 indexed hash, + string name, + bytes encryptedEmail, + address indexed upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes checkData, + uint96 amount, + uint8 indexed source + ); + + event RegistrationApproved(bytes32 indexed hash, string displayName, uint256 indexed upkeepId); + + event RegistrationRejected(bytes32 indexed hash); + + event AutoApproveAllowedSenderSet(address indexed senderAddress, bool allowed); + + event ConfigChanged( + AutoApproveType autoApproveConfigType, + uint32 autoApproveMaxAllowed, + address keeperRegistry, + uint96 minLINKJuels + ); + + error InvalidAdminAddress(); + error RequestNotFound(); + error HashMismatch(); + error OnlyAdminOrOwner(); + error InsufficientPayment(); + error RegistrationRequestFailed(); + error OnlyLink(); + error AmountMismatch(); + error SenderMismatch(); + error FunctionNotPermitted(); + error LinkTransferFailed(address to); + error InvalidDataLength(); + + /* + * @param LINKAddress Address of Link token + * @param autoApproveConfigType setting for auto-approve registrations + * @param autoApproveMaxAllowed max number of registrations that can be auto approved + * @param keeperRegistry keeper registry address + * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with + */ + constructor( + address LINKAddress, + AutoApproveType autoApproveConfigType, + uint16 autoApproveMaxAllowed, + address keeperRegistry, + uint96 minLINKJuels + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(LINKAddress); + setRegistrationConfig(autoApproveConfigType, autoApproveMaxAllowed, keeperRegistry, minLINKJuels); + } + + //EXTERNAL + + /** + * @notice register can only be called through transferAndCall on LINK contract + * @param name string of the upkeep to be registered + * @param encryptedEmail email address of upkeep contact + * @param upkeepContract address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when performing upkeep + * @param adminAddress address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + * @param amount quantity of LINK upkeep is funded with (specified in Juels) + * @param source application sending this request + * @param sender address of the sender making the request + */ + function register( + string memory name, + bytes calldata encryptedEmail, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + uint8 source, + address sender + ) external onlyLINK { + if (adminAddress == address(0)) { + revert InvalidAdminAddress(); + } + bytes32 hash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); + + emit RegistrationRequested( + hash, + name, + encryptedEmail, + upkeepContract, + gasLimit, + adminAddress, + checkData, + amount, + source + ); + + Config memory config = s_config; + if (_shouldAutoApprove(config, sender)) { + s_config.approvedCount = config.approvedCount + 1; + + _approve(name, upkeepContract, gasLimit, adminAddress, checkData, amount, hash); + } else { + uint96 newBalance = s_pendingRequests[hash].balance + amount; + s_pendingRequests[hash] = PendingRequest({admin: adminAddress, balance: newBalance}); + } + } + + /** + * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event + */ + function approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + bytes32 hash + ) external onlyOwner { + PendingRequest memory request = s_pendingRequests[hash]; + if (request.admin == address(0)) { + revert RequestNotFound(); + } + bytes32 expectedHash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); + if (hash != expectedHash) { + revert HashMismatch(); + } + delete s_pendingRequests[hash]; + _approve(name, upkeepContract, gasLimit, adminAddress, checkData, request.balance, hash); + } + + /** + * @notice cancel will remove a registration request and return the refunds to the msg.sender + * @param hash the request hash + */ + function cancel(bytes32 hash) external { + PendingRequest memory request = s_pendingRequests[hash]; + if (!(msg.sender == request.admin || msg.sender == owner())) { + revert OnlyAdminOrOwner(); + } + if (request.admin == address(0)) { + revert RequestNotFound(); + } + delete s_pendingRequests[hash]; + bool success = LINK.transfer(msg.sender, request.balance); + if (!success) { + revert LinkTransferFailed(msg.sender); + } + emit RegistrationRejected(hash); + } + + /** + * @notice owner calls this function to set if registration requests should be sent directly to the Keeper Registry + * @param autoApproveConfigType setting for auto-approve registrations + * note: autoApproveAllowedSenders list persists across config changes irrespective of type + * @param autoApproveMaxAllowed max number of registrations that can be auto approved + * @param keeperRegistry new keeper registry address + * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with + */ + function setRegistrationConfig( + AutoApproveType autoApproveConfigType, + uint16 autoApproveMaxAllowed, + address keeperRegistry, + uint96 minLINKJuels + ) public onlyOwner { + uint32 approvedCount = s_config.approvedCount; + s_config = Config({ + autoApproveConfigType: autoApproveConfigType, + autoApproveMaxAllowed: autoApproveMaxAllowed, + approvedCount: approvedCount, + minLINKJuels: minLINKJuels, + keeperRegistry: KeeperRegistryBaseInterface(keeperRegistry) + }); + + emit ConfigChanged(autoApproveConfigType, autoApproveMaxAllowed, keeperRegistry, minLINKJuels); + } + + /** + * @notice owner calls this function to set allowlist status for senderAddress + * @param senderAddress senderAddress to set the allowlist status for + * @param allowed true if senderAddress needs to be added to allowlist, false if needs to be removed + */ + function setAutoApproveAllowedSender(address senderAddress, bool allowed) external onlyOwner { + s_autoApproveAllowedSenders[senderAddress] = allowed; + + emit AutoApproveAllowedSenderSet(senderAddress, allowed); + } + + /** + * @notice read the allowlist status of senderAddress + * @param senderAddress address to read the allowlist status for + */ + function getAutoApproveAllowedSender(address senderAddress) external view returns (bool) { + return s_autoApproveAllowedSenders[senderAddress]; + } + + /** + * @notice read the current registration configuration + */ + function getRegistrationConfig() + external + view + returns ( + AutoApproveType autoApproveConfigType, + uint32 autoApproveMaxAllowed, + uint32 approvedCount, + address keeperRegistry, + uint256 minLINKJuels + ) + { + Config memory config = s_config; + return ( + config.autoApproveConfigType, + config.autoApproveMaxAllowed, + config.approvedCount, + address(config.keeperRegistry), + config.minLINKJuels + ); + } + + /** + * @notice gets the admin address and the current balance of a registration request + */ + function getPendingRequest(bytes32 hash) external view returns (address, uint96) { + PendingRequest memory request = s_pendingRequests[hash]; + return (request.admin, request.balance); + } + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @param sender Address of the sender transfering LINK + * @param amount Amount of LINK sent (specified in Juels) + * @param data Payload of the transaction + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) + external + override + onlyLINK + permittedFunctionsForLINK(data) + isActualAmount(amount, data) + isActualSender(sender, data) + { + if (data.length < 292) revert InvalidDataLength(); + if (amount < s_config.minLINKJuels) { + revert InsufficientPayment(); + } + (bool success, ) = address(this).delegatecall(data); + // calls register + if (!success) { + revert RegistrationRequestFailed(); + } + } + + //PRIVATE + + /** + * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event + */ + function _approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + bytes32 hash + ) private { + KeeperRegistryBaseInterface keeperRegistry = s_config.keeperRegistry; + + // register upkeep + uint256 upkeepId = keeperRegistry.registerUpkeep(upkeepContract, gasLimit, adminAddress, checkData); + // fund upkeep + bool success = LINK.transferAndCall(address(keeperRegistry), amount, abi.encode(upkeepId)); + if (!success) { + revert LinkTransferFailed(address(keeperRegistry)); + } + + emit RegistrationApproved(hash, name, upkeepId); + } + + /** + * @dev verify sender allowlist if needed and check max limit + */ + function _shouldAutoApprove(Config memory config, address sender) private returns (bool) { + if (config.autoApproveConfigType == AutoApproveType.DISABLED) { + return false; + } + if ( + config.autoApproveConfigType == AutoApproveType.ENABLED_SENDER_ALLOWLIST && (!s_autoApproveAllowedSenders[sender]) + ) { + return false; + } + if (config.approvedCount < config.autoApproveMaxAllowed) { + return true; + } + return false; + } + + //MODIFIERS + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + if (msg.sender != address(LINK)) { + revert OnlyLink(); + } + _; + } + + /** + * @dev Reverts if the given data does not begin with the `register` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory _data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(_data, 32)) // First 32 bytes contain length of data + } + if (funcSelector != REGISTER_REQUEST_SELECTOR) { + revert FunctionNotPermitted(); + } + _; + } + + /** + * @dev Reverts if the actual amount passed does not match the expected amount + * @param expected amount that should match the actual amount + * @param data bytes + */ + modifier isActualAmount(uint256 expected, bytes memory data) { + uint256 actual; + assembly { + actual := mload(add(data, 228)) + } + if (expected != actual) { + revert AmountMismatch(); + } + _; + } + + /** + * @dev Reverts if the actual sender address does not match the expected sender address + * @param expected address that should match the actual sender address + * @param data bytes + */ + modifier isActualSender(address expected, bytes memory data) { + address actual; + assembly { + actual := mload(add(data, 292)) + } + if (expected != actual) { + revert SenderMismatch(); + } + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistry.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistry.sol new file mode 100644 index 0000000..f834de9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistry.sol @@ -0,0 +1,939 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/security/Pausable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "./KeeperBase.sol"; +import "./ConfirmedOwner.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./interfaces/AggregatorV3Interface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperCompatibleInterface.sol"; +import "./interfaces/KeeperRegistryInterface.sol"; +import "./interfaces/MigratableKeeperRegistryInterface.sol"; +import "./interfaces/UpkeepTranscoderInterface.sol"; +import "./interfaces/ERC677ReceiverInterface.sol"; + +/** + * @notice Registry for adding work for Chainlink Keepers to perform on client + * contracts. Clients must support the Upkeep interface. + */ +contract KeeperRegistry is + TypeAndVersionInterface, + ConfirmedOwner, + KeeperBase, + ReentrancyGuard, + Pausable, + KeeperRegistryExecutableInterface, + MigratableKeeperRegistryInterface, + ERC677ReceiverInterface +{ + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + + address private constant ZERO_ADDRESS = address(0); + address private constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 private constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 private constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + uint256 private constant PERFORM_GAS_MIN = 2_300; + uint256 private constant CANCELATION_DELAY = 50; + uint256 private constant PERFORM_GAS_CUSHION = 5_000; + uint256 private constant REGISTRY_GAS_OVERHEAD = 80_000; + uint256 private constant PPB_BASE = 1_000_000_000; + uint64 private constant UINT64_MAX = 2**64 - 1; + uint96 private constant LINK_TOTAL_SUPPLY = 1e27; + + address[] private s_keeperList; + EnumerableSet.UintSet private s_upkeepIDs; + mapping(uint256 => Upkeep) private s_upkeep; + mapping(address => KeeperInfo) private s_keeperInfo; + mapping(address => address) private s_proposedPayee; + mapping(uint256 => bytes) private s_checkData; + mapping(address => MigrationPermission) private s_peerRegistryMigrationPermission; + Storage private s_storage; + uint256 private s_fallbackGasPrice; // not in config object for gas savings + uint256 private s_fallbackLinkPrice; // not in config object for gas savings + uint96 private s_ownerLinkBalance; + uint256 private s_expectedLinkBalance; + address private s_transcoder; + address private s_registrar; + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + AggregatorV3Interface public immutable FAST_GAS_FEED; + + /** + * @notice versions: + * - KeeperRegistry 1.2.0: allow funding within performUpkeep + * : allow configurable registry maxPerformGas + * : add function to let admin change upkeep gas limit + * : add minUpkeepSpend requirement + : upgrade to solidity v0.8 + * - KeeperRegistry 1.1.0: added flatFeeMicroLink + * - KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistry 1.2.0"; + + error CannotCancel(); + error UpkeepNotActive(); + error MigrationNotPermitted(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error NotAContract(); + error PaymentGreaterThanAllLINK(); + error OnlyActiveKeepers(); + error InsufficientFunds(); + error KeepersMustTakeTurns(); + error ParameterLengthError(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByLINKToken(); + error InvalidPayee(); + error DuplicateEntry(); + error ValueNotChanged(); + error IndexOutOfRange(); + error TranscoderNotSet(); + error ArrayHasNoEntries(); + error GasLimitOutsideRange(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedPayee(); + error GasLimitCanOnlyIncrease(); + error OnlyCallableByAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error InvalidRecipient(); + error InvalidDataLength(); + error TargetCheckReverted(bytes reason); + + enum MigrationPermission { + NONE, + OUTGOING, + INCOMING, + BIDIRECTIONAL + } + + /** + * @notice storage of the registry, contains a mix of config and state data + */ + struct Storage { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; // 1 evm word + uint32 maxPerformGas; + uint32 nonce; // 2 evm words + } + + struct Upkeep { + uint96 balance; + address lastKeeper; // 1 storage slot full + uint32 executeGas; + uint64 maxValidBlocknumber; + address target; // 2 storage slots full + uint96 amountSpent; + address admin; // 3 storage slots full + } + + struct KeeperInfo { + address payee; + uint96 balance; + bool active; + } + + struct PerformParams { + address from; + uint256 id; + bytes performData; + uint256 maxLinkPayment; + uint256 gasLimit; + uint256 adjustedGasWei; + uint256 linkEth; + } + + event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + address indexed from, + uint96 payment, + bytes performData + ); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event OwnerFundsWithdrawn(uint96 amount); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event ConfigSet(Config config); + event KeepersUpdated(address[] keepers, address[] payees); + event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); + event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + + /** + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + * @param config registry config settings + */ + constructor( + address link, + address linkEthFeed, + address fastGasFeed, + Config memory config + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); + setConfig(config); + } + + // ACTIONS + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external override onlyOwnerOrRegistrar returns (uint256 id) { + id = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), s_storage.nonce))); + _createUpkeep(id, target, gasLimit, admin, 0, checkData); + s_storage.nonce++; + emit UpkeepRegistered(id, gasLimit, admin); + return id; + } + + /** + * @notice simulated by keepers via eth_call to see if the upkeep needs to be + * performed. If upkeep is needed, the call then simulates performUpkeep + * to make sure it succeeds. Finally, it returns the success status along with + * payment information and the perform data payload. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function checkUpkeep(uint256 id, address from) + external + override + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + Upkeep memory upkeep = s_upkeep[id]; + + bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); + (bool success, bytes memory result) = upkeep.target.call{gas: s_storage.checkGasLimit}(callData); + + if (!success) revert TargetCheckReverted(result); + + (success, performData) = abi.decode(result, (bool, bytes)); + if (!success) revert UpkeepNotNeeded(); + + PerformParams memory params = _generatePerformParams(from, id, performData, false); + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + return (performData, params.maxLinkPayment, params.gasLimit, params.adjustedGasWei, params.linkEth); + } + + /** + * @notice executes the upkeep with the perform data returned from + * checkUpkeep, validates the keeper's permissions, and pays the keeper. + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + */ + function performUpkeep(uint256 id, bytes calldata performData) + external + override + whenNotPaused + returns (bool success) + { + return _performUpkeepWithParams(_generatePerformParams(msg.sender, id, performData, true)); + } + + /** + * @notice prevent an upkeep from being performed in the future + * @param id upkeep to be canceled + */ + function cancelUpkeep(uint256 id) external override { + uint64 maxValid = s_upkeep[id].maxValidBlocknumber; + bool canceled = maxValid != UINT64_MAX; + bool isOwner = msg.sender == owner(); + + if (canceled && !(isOwner && maxValid > block.number)) revert CannotCancel(); + if (!isOwner && msg.sender != s_upkeep[id].admin) revert OnlyCallableByOwnerOrAdmin(); + + uint256 height = block.number; + if (!isOwner) { + height = height + CANCELATION_DELAY; + } + s_upkeep[id].maxValidBlocknumber = uint64(height); + s_upkeepIDs.remove(id); + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @notice adds LINK funding for an upkeep by transferring from the sender's + * LINK balance + * @param id upkeep to fund + * @param amount number of LINK to transfer + */ + function addFunds(uint256 id, uint96 amount) external override onlyActiveUpkeep(id) { + s_upkeep[id].balance = s_upkeep[id].balance + amount; + s_expectedLinkBalance = s_expectedLinkBalance + amount; + LINK.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external override { + if (msg.sender != address(LINK)) revert OnlyCallableByLINKToken(); + if (data.length != 32) revert InvalidDataLength(); + uint256 id = abi.decode(data, (uint256)); + if (s_upkeep[id].maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); + + s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); + s_expectedLinkBalance = s_expectedLinkBalance + amount; + + emit FundsAdded(id, sender, uint96(amount)); + } + + /** + * @notice removes funding from a canceled upkeep + * @param id upkeep to withdraw funds from + * @param to destination address for sending remaining funds + */ + function withdrawFunds(uint256 id, address to) external validRecipient(to) onlyUpkeepAdmin(id) { + if (s_upkeep[id].maxValidBlocknumber > block.number) revert UpkeepNotCanceled(); + + uint96 minUpkeepSpend = s_storage.minUpkeepSpend; + uint96 amountLeft = s_upkeep[id].balance; + uint96 amountSpent = s_upkeep[id].amountSpent; + + uint96 cancellationFee = 0; + // cancellationFee is supposed to be min(max(minUpkeepSpend - amountSpent,0), amountLeft) + if (amountSpent < minUpkeepSpend) { + cancellationFee = minUpkeepSpend - amountSpent; + if (cancellationFee > amountLeft) { + cancellationFee = amountLeft; + } + } + uint96 amountToWithdraw = amountLeft - cancellationFee; + + s_upkeep[id].balance = 0; + s_ownerLinkBalance = s_ownerLinkBalance + cancellationFee; + + s_expectedLinkBalance = s_expectedLinkBalance - amountToWithdraw; + emit FundsWithdrawn(id, amountToWithdraw, to); + + LINK.transfer(to, amountToWithdraw); + } + + /** + * @notice withdraws LINK funds collected through cancellation fees + */ + function withdrawOwnerFunds() external onlyOwner { + uint96 amount = s_ownerLinkBalance; + + s_expectedLinkBalance = s_expectedLinkBalance - amount; + s_ownerLinkBalance = 0; + + emit OwnerFundsWithdrawn(amount); + LINK.transfer(msg.sender, amount); + } + + /** + * @notice allows the admin of an upkeep to modify gas limit + * @param id upkeep to be change the gas limit for + * @param gasLimit new gas limit for the upkeep + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external override onlyActiveUpkeep(id) onlyUpkeepAdmin(id) { + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + + s_upkeep[id].executeGas = gasLimit; + + emit UpkeepGasLimitSet(id, gasLimit); + } + + /** + * @notice recovers LINK funds improperly transferred to the registry + * @dev In principle this function’s execution cost could exceed block + * gas limit. However, in our anticipated deployment, the number of upkeeps and + * keepers will be low enough to avoid this problem. + */ + function recoverFunds() external onlyOwner { + uint256 total = LINK.balanceOf(address(this)); + LINK.transfer(msg.sender, total - s_expectedLinkBalance); + } + + /** + * @notice withdraws a keeper's payment, callable only by the keeper's payee + * @param from keeper address + * @param to address to send the payment to + */ + function withdrawPayment(address from, address to) external validRecipient(to) { + KeeperInfo memory keeper = s_keeperInfo[from]; + if (keeper.payee != msg.sender) revert OnlyCallableByPayee(); + + s_keeperInfo[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance - keeper.balance; + emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); + + LINK.transfer(to, keeper.balance); + } + + /** + * @notice proposes the safe transfer of a keeper's payee to another address + * @param keeper address of the keeper to transfer payee role + * @param proposed address to nominate for next payeeship + */ + function transferPayeeship(address keeper, address proposed) external { + if (s_keeperInfo[keeper].payee != msg.sender) revert OnlyCallableByPayee(); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedPayee[keeper] != proposed) { + s_proposedPayee[keeper] = proposed; + emit PayeeshipTransferRequested(keeper, msg.sender, proposed); + } + } + + /** + * @notice accepts the safe transfer of payee role for a keeper + * @param keeper address to accept the payee role for + */ + function acceptPayeeship(address keeper) external { + if (s_proposedPayee[keeper] != msg.sender) revert OnlyCallableByProposedPayee(); + address past = s_keeperInfo[keeper].payee; + s_keeperInfo[keeper].payee = msg.sender; + s_proposedPayee[keeper] = ZERO_ADDRESS; + + emit PayeeshipTransferred(keeper, past, msg.sender); + } + + /** + * @notice signals to keepers that they should not perform upkeeps until the + * contract has been unpaused + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice signals to keepers that they can perform upkeeps once again after + * having been paused + */ + function unpause() external onlyOwner { + _unpause(); + } + + // SETTERS + + /** + * @notice updates the configuration of the registry + * @param config registry config fields + */ + function setConfig(Config memory config) public onlyOwner { + if (config.maxPerformGas < s_storage.maxPerformGas) revert GasLimitCanOnlyIncrease(); + s_storage = Storage({ + paymentPremiumPPB: config.paymentPremiumPPB, + flatFeeMicroLink: config.flatFeeMicroLink, + blockCountPerTurn: config.blockCountPerTurn, + checkGasLimit: config.checkGasLimit, + stalenessSeconds: config.stalenessSeconds, + gasCeilingMultiplier: config.gasCeilingMultiplier, + minUpkeepSpend: config.minUpkeepSpend, + maxPerformGas: config.maxPerformGas, + nonce: s_storage.nonce + }); + s_fallbackGasPrice = config.fallbackGasPrice; + s_fallbackLinkPrice = config.fallbackLinkPrice; + s_transcoder = config.transcoder; + s_registrar = config.registrar; + emit ConfigSet(config); + } + + /** + * @notice update the list of keepers allowed to perform upkeep + * @param keepers list of addresses allowed to perform upkeep + * @param payees addresses corresponding to keepers who are allowed to + * move payments which have been accrued + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { + if (keepers.length != payees.length || keepers.length < 2) revert ParameterLengthError(); + for (uint256 i = 0; i < s_keeperList.length; i++) { + address keeper = s_keeperList[i]; + s_keeperInfo[keeper].active = false; + } + for (uint256 i = 0; i < keepers.length; i++) { + address keeper = keepers[i]; + KeeperInfo storage s_keeper = s_keeperInfo[keeper]; + address oldPayee = s_keeper.payee; + address newPayee = payees[i]; + if ( + (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) + ) revert InvalidPayee(); + if (s_keeper.active) revert DuplicateEntry(); + s_keeper.active = true; + if (newPayee != IGNORE_ADDRESS) { + s_keeper.payee = newPayee; + } + } + s_keeperList = keepers; + emit KeepersUpdated(keepers, payees); + } + + // GETTERS + + /** + * @notice read all of the details about an upkeep + */ + function getUpkeep(uint256 id) + external + view + override + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent + ) + { + Upkeep memory reg = s_upkeep[id]; + return ( + reg.target, + reg.executeGas, + s_checkData[id], + reg.balance, + reg.lastKeeper, + reg.admin, + reg.maxValidBlocknumber, + reg.amountSpent + ); + } + + /** + * @notice retrieve active upkeep IDs + * @param startIndex starting index in list + * @param maxCount max count to retrieve (0 = unlimited) + * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one + * should consider keeping the blockheight constant to ensure a wholistic picture of the contract state + */ + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view override returns (uint256[] memory) { + uint256 maxIdx = s_upkeepIDs.length(); + if (startIndex >= maxIdx) revert IndexOutOfRange(); + if (maxCount == 0) { + maxCount = maxIdx - startIndex; + } + uint256[] memory ids = new uint256[](maxCount); + for (uint256 idx = 0; idx < maxCount; idx++) { + ids[idx] = s_upkeepIDs.at(startIndex + idx); + } + return ids; + } + + /** + * @notice read the current info about any keeper address + */ + function getKeeperInfo(address query) + external + view + override + returns ( + address payee, + bool active, + uint96 balance + ) + { + KeeperInfo memory keeper = s_keeperInfo[query]; + return (keeper.payee, keeper.active, keeper.balance); + } + + /** + * @notice read the current state of the registry + */ + function getState() + external + view + override + returns ( + State memory state, + Config memory config, + address[] memory keepers + ) + { + Storage memory store = s_storage; + state.nonce = store.nonce; + state.ownerLinkBalance = s_ownerLinkBalance; + state.expectedLinkBalance = s_expectedLinkBalance; + state.numUpkeeps = s_upkeepIDs.length(); + config.paymentPremiumPPB = store.paymentPremiumPPB; + config.flatFeeMicroLink = store.flatFeeMicroLink; + config.blockCountPerTurn = store.blockCountPerTurn; + config.checkGasLimit = store.checkGasLimit; + config.stalenessSeconds = store.stalenessSeconds; + config.gasCeilingMultiplier = store.gasCeilingMultiplier; + config.minUpkeepSpend = store.minUpkeepSpend; + config.maxPerformGas = store.maxPerformGas; + config.fallbackGasPrice = s_fallbackGasPrice; + config.fallbackLinkPrice = s_fallbackLinkPrice; + config.transcoder = s_transcoder; + config.registrar = s_registrar; + return (state, config, s_keeperList); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { + return getMaxPaymentForGas(s_upkeep[id].executeGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + * @param gasLimit the gas to calculate payment for + */ + function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { + (uint256 gasWei, uint256 linkEth) = _getFeedData(); + uint256 adjustedGasWei = _adjustGasPrice(gasWei, false); + return _calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + } + + /** + * @notice retrieves the migration permission for a peer registry + */ + function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { + return s_peerRegistryMigrationPermission[peer]; + } + + /** + * @notice sets the peer registry migration permission + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { + s_peerRegistryMigrationPermission[peer] = permission; + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external override { + if ( + s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && + s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + if (s_transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); + if (ids.length == 0) revert ArrayHasNoEntries(); + uint256 id; + Upkeep memory upkeep; + uint256 totalBalanceRemaining; + bytes[] memory checkDatas = new bytes[](ids.length); + Upkeep[] memory upkeeps = new Upkeep[](ids.length); + for (uint256 idx = 0; idx < ids.length; idx++) { + id = ids[idx]; + upkeep = s_upkeep[id]; + if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); + upkeeps[idx] = upkeep; + checkDatas[idx] = s_checkData[id]; + totalBalanceRemaining = totalBalanceRemaining + upkeep.balance; + delete s_upkeep[id]; + delete s_checkData[id]; + s_upkeepIDs.remove(id); + emit UpkeepMigrated(id, upkeep.balance, destination); + } + s_expectedLinkBalance = s_expectedLinkBalance - totalBalanceRemaining; + bytes memory encodedUpkeeps = abi.encode(ids, upkeeps, checkDatas); + MigratableKeeperRegistryInterface(destination).receiveUpkeeps( + UpkeepTranscoderInterface(s_transcoder).transcodeUpkeeps( + UpkeepFormat.V1, + MigratableKeeperRegistryInterface(destination).upkeepTranscoderVersion(), + encodedUpkeeps + ) + ); + LINK.transfer(destination, totalBalanceRemaining); + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + UpkeepFormat public constant override upkeepTranscoderVersion = UpkeepFormat.V1; + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external override { + if ( + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + (uint256[] memory ids, Upkeep[] memory upkeeps, bytes[] memory checkDatas) = abi.decode( + encodedUpkeeps, + (uint256[], Upkeep[], bytes[]) + ); + for (uint256 idx = 0; idx < ids.length; idx++) { + _createUpkeep( + ids[idx], + upkeeps[idx].target, + upkeeps[idx].executeGas, + upkeeps[idx].admin, + upkeeps[idx].balance, + checkDatas[idx] + ); + emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); + } + } + + /** + * @notice creates a new upkeep with the given fields + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function _createUpkeep( + uint256 id, + address target, + uint32 gasLimit, + address admin, + uint96 balance, + bytes memory checkData + ) internal whenNotPaused { + if (!target.isContract()) revert NotAContract(); + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + s_upkeep[id] = Upkeep({ + target: target, + executeGas: gasLimit, + balance: balance, + admin: admin, + maxValidBlocknumber: UINT64_MAX, + lastKeeper: ZERO_ADDRESS, + amountSpent: 0 + }); + s_expectedLinkBalance = s_expectedLinkBalance + balance; + s_checkData[id] = checkData; + s_upkeepIDs.add(id); + } + + /** + * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function _getFeedData() private view returns (uint256 gasWei, uint256 linkEth) { + uint32 stalenessSeconds = s_storage.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + linkEth = s_fallbackLinkPrice; + } else { + linkEth = uint256(feedValue); + } + return (gasWei, linkEth); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + */ + function _calculatePaymentAmount( + uint256 gasLimit, + uint256 gasWei, + uint256 linkEth + ) private view returns (uint96 payment) { + uint256 weiForGas = gasWei * (gasLimit + REGISTRY_GAS_OVERHEAD); + uint256 premium = PPB_BASE + s_storage.paymentPremiumPPB; + uint256 total = ((weiForGas * (1e9) * (premium)) / (linkEth)) + (uint256(s_storage.flatFeeMicroLink) * (1e12)); + if (total > LINK_TOTAL_SUPPLY) revert PaymentGreaterThanAllLINK(); + return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available + */ + function _callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= PERFORM_GAS_CUSHION and check for underflow + if lt(g, PERFORM_GAS_CUSHION) { + revert(0, 0) + } + g := sub(g, PERFORM_GAS_CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * keeper and the exact gas required by the Upkeep + */ + function _performUpkeepWithParams(PerformParams memory params) + private + nonReentrant + validUpkeep(params.id) + returns (bool success) + { + Upkeep memory upkeep = s_upkeep[params.id]; + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + uint256 gasUsed = gasleft(); + bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); + success = _callWithExactGas(params.gasLimit, upkeep.target, callData); + gasUsed = gasUsed - gasleft(); + + uint96 payment = _calculatePaymentAmount(gasUsed, params.adjustedGasWei, params.linkEth); + + s_upkeep[params.id].balance = s_upkeep[params.id].balance - payment; + s_upkeep[params.id].amountSpent = s_upkeep[params.id].amountSpent + payment; + s_upkeep[params.id].lastKeeper = params.from; + s_keeperInfo[params.from].balance = s_keeperInfo[params.from].balance + payment; + + emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); + return success; + } + + /** + * @dev ensures all required checks are passed before an upkeep is performed + */ + function _prePerformUpkeep( + Upkeep memory upkeep, + address from, + uint256 maxLinkPayment + ) private view { + if (!s_keeperInfo[from].active) revert OnlyActiveKeepers(); + if (upkeep.balance < maxLinkPayment) revert InsufficientFunds(); + if (upkeep.lastKeeper == from) revert KeepersMustTakeTurns(); + } + + /** + * @dev adjusts the gas price to min(ceiling, tx.gasprice) or just uses the ceiling if tx.gasprice is disabled + */ + function _adjustGasPrice(uint256 gasWei, bool useTxGasPrice) private view returns (uint256 adjustedPrice) { + adjustedPrice = gasWei * s_storage.gasCeilingMultiplier; + if (useTxGasPrice && tx.gasprice < adjustedPrice) { + adjustedPrice = tx.gasprice; + } + } + + /** + * @dev generates a PerformParams struct for use in _performUpkeepWithParams() + */ + function _generatePerformParams( + address from, + uint256 id, + bytes memory performData, + bool useTxGasPrice + ) private view returns (PerformParams memory) { + uint256 gasLimit = s_upkeep[id].executeGas; + (uint256 gasWei, uint256 linkEth) = _getFeedData(); + uint256 adjustedGasWei = _adjustGasPrice(gasWei, useTxGasPrice); + uint96 maxLinkPayment = _calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + + return + PerformParams({ + from: from, + id: id, + performData: performData, + maxLinkPayment: maxLinkPayment, + gasLimit: gasLimit, + adjustedGasWei: adjustedGasWei, + linkEth: linkEth + }); + } + + // MODIFIERS + + /** + * @dev ensures a upkeep is valid + */ + modifier validUpkeep(uint256 id) { + if (s_upkeep[id].maxValidBlocknumber <= block.number) revert UpkeepNotActive(); + _; + } + + /** + * @dev Reverts if called by anyone other than the admin of upkeep #id + */ + modifier onlyUpkeepAdmin(uint256 id) { + if (msg.sender != s_upkeep[id].admin) revert OnlyCallableByAdmin(); + _; + } + + /** + * @dev Reverts if called on a cancelled upkeep + */ + modifier onlyActiveUpkeep(uint256 id) { + if (s_upkeep[id].maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); + _; + } + + /** + * @dev ensures that burns don't accidentally happen by sending to the zero + * address + */ + modifier validRecipient(address to) { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + _; + } + + /** + * @dev Reverts if called by anyone other than the contract owner or registrar. + */ + modifier onlyOwnerOrRegistrar() { + if (msg.sender != owner() && msg.sender != s_registrar) revert OnlyCallableByOwnerOrRegistrar(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistry1_2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistry1_2.sol new file mode 100644 index 0000000..8465226 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeeperRegistry1_2.sol @@ -0,0 +1,939 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/security/Pausable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "./KeeperBase.sol"; +import "./ConfirmedOwner.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./interfaces/AggregatorV3Interface.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperCompatibleInterface.sol"; +import "./interfaces/KeeperRegistryInterface1_2.sol"; +import "./interfaces/MigratableKeeperRegistryInterface.sol"; +import "./interfaces/UpkeepTranscoderInterface.sol"; +import "./interfaces/ERC677ReceiverInterface.sol"; + +/** + * @notice Registry for adding work for Chainlink Keepers to perform on client + * contracts. Clients must support the Upkeep interface. + */ +contract KeeperRegistry1_2 is + TypeAndVersionInterface, + ConfirmedOwner, + KeeperBase, + ReentrancyGuard, + Pausable, + KeeperRegistryExecutableInterface, + MigratableKeeperRegistryInterface, + ERC677ReceiverInterface +{ + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + + address private constant ZERO_ADDRESS = address(0); + address private constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 private constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 private constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + uint256 private constant PERFORM_GAS_MIN = 2_300; + uint256 private constant CANCELATION_DELAY = 50; + uint256 private constant PERFORM_GAS_CUSHION = 5_000; + uint256 private constant REGISTRY_GAS_OVERHEAD = 80_000; + uint256 private constant PPB_BASE = 1_000_000_000; + uint64 private constant UINT64_MAX = 2**64 - 1; + uint96 private constant LINK_TOTAL_SUPPLY = 1e27; + + address[] private s_keeperList; + EnumerableSet.UintSet private s_upkeepIDs; + mapping(uint256 => Upkeep) private s_upkeep; + mapping(address => KeeperInfo) private s_keeperInfo; + mapping(address => address) private s_proposedPayee; + mapping(uint256 => bytes) private s_checkData; + mapping(address => MigrationPermission) private s_peerRegistryMigrationPermission; + Storage private s_storage; + uint256 private s_fallbackGasPrice; // not in config object for gas savings + uint256 private s_fallbackLinkPrice; // not in config object for gas savings + uint96 private s_ownerLinkBalance; + uint256 private s_expectedLinkBalance; + address private s_transcoder; + address private s_registrar; + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + AggregatorV3Interface public immutable FAST_GAS_FEED; + + /** + * @notice versions: + * - KeeperRegistry 1.2.0: allow funding within performUpkeep + * : allow configurable registry maxPerformGas + * : add function to let admin change upkeep gas limit + * : add minUpkeepSpend requirement + : upgrade to solidity v0.8 + * - KeeperRegistry 1.1.0: added flatFeeMicroLink + * - KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistry 1.2.0"; + + error CannotCancel(); + error UpkeepNotActive(); + error MigrationNotPermitted(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error NotAContract(); + error PaymentGreaterThanAllLINK(); + error OnlyActiveKeepers(); + error InsufficientFunds(); + error KeepersMustTakeTurns(); + error ParameterLengthError(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByLINKToken(); + error InvalidPayee(); + error DuplicateEntry(); + error ValueNotChanged(); + error IndexOutOfRange(); + error TranscoderNotSet(); + error ArrayHasNoEntries(); + error GasLimitOutsideRange(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedPayee(); + error GasLimitCanOnlyIncrease(); + error OnlyCallableByAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error InvalidRecipient(); + error InvalidDataLength(); + error TargetCheckReverted(bytes reason); + + enum MigrationPermission { + NONE, + OUTGOING, + INCOMING, + BIDIRECTIONAL + } + + /** + * @notice storage of the registry, contains a mix of config and state data + */ + struct Storage { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; // 1 evm word + uint32 maxPerformGas; + uint32 nonce; // 2 evm words + } + + struct Upkeep { + uint96 balance; + address lastKeeper; // 1 storage slot full + uint32 executeGas; + uint64 maxValidBlocknumber; + address target; // 2 storage slots full + uint96 amountSpent; + address admin; // 3 storage slots full + } + + struct KeeperInfo { + address payee; + uint96 balance; + bool active; + } + + struct PerformParams { + address from; + uint256 id; + bytes performData; + uint256 maxLinkPayment; + uint256 gasLimit; + uint256 adjustedGasWei; + uint256 linkEth; + } + + event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + address indexed from, + uint96 payment, + bytes performData + ); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event OwnerFundsWithdrawn(uint96 amount); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event ConfigSet(Config config); + event KeepersUpdated(address[] keepers, address[] payees); + event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); + event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + + /** + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + * @param config registry config settings + */ + constructor( + address link, + address linkEthFeed, + address fastGasFeed, + Config memory config + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); + setConfig(config); + } + + // ACTIONS + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external override onlyOwnerOrRegistrar returns (uint256 id) { + id = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), s_storage.nonce))); + _createUpkeep(id, target, gasLimit, admin, 0, checkData); + s_storage.nonce++; + emit UpkeepRegistered(id, gasLimit, admin); + return id; + } + + /** + * @notice simulated by keepers via eth_call to see if the upkeep needs to be + * performed. If upkeep is needed, the call then simulates performUpkeep + * to make sure it succeeds. Finally, it returns the success status along with + * payment information and the perform data payload. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function checkUpkeep(uint256 id, address from) + external + override + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + Upkeep memory upkeep = s_upkeep[id]; + + bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); + (bool success, bytes memory result) = upkeep.target.call{gas: s_storage.checkGasLimit}(callData); + + if (!success) revert TargetCheckReverted(result); + + (success, performData) = abi.decode(result, (bool, bytes)); + if (!success) revert UpkeepNotNeeded(); + + PerformParams memory params = _generatePerformParams(from, id, performData, false); + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + return (performData, params.maxLinkPayment, params.gasLimit, params.adjustedGasWei, params.linkEth); + } + + /** + * @notice executes the upkeep with the perform data returned from + * checkUpkeep, validates the keeper's permissions, and pays the keeper. + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + */ + function performUpkeep(uint256 id, bytes calldata performData) + external + override + whenNotPaused + returns (bool success) + { + return _performUpkeepWithParams(_generatePerformParams(msg.sender, id, performData, true)); + } + + /** + * @notice prevent an upkeep from being performed in the future + * @param id upkeep to be canceled + */ + function cancelUpkeep(uint256 id) external override { + uint64 maxValid = s_upkeep[id].maxValidBlocknumber; + bool canceled = maxValid != UINT64_MAX; + bool isOwner = msg.sender == owner(); + + if (canceled && !(isOwner && maxValid > block.number)) revert CannotCancel(); + if (!isOwner && msg.sender != s_upkeep[id].admin) revert OnlyCallableByOwnerOrAdmin(); + + uint256 height = block.number; + if (!isOwner) { + height = height + CANCELATION_DELAY; + } + s_upkeep[id].maxValidBlocknumber = uint64(height); + s_upkeepIDs.remove(id); + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @notice adds LINK funding for an upkeep by transferring from the sender's + * LINK balance + * @param id upkeep to fund + * @param amount number of LINK to transfer + */ + function addFunds(uint256 id, uint96 amount) external override onlyActiveUpkeep(id) { + s_upkeep[id].balance = s_upkeep[id].balance + amount; + s_expectedLinkBalance = s_expectedLinkBalance + amount; + LINK.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external override { + if (msg.sender != address(LINK)) revert OnlyCallableByLINKToken(); + if (data.length != 32) revert InvalidDataLength(); + uint256 id = abi.decode(data, (uint256)); + if (s_upkeep[id].maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); + + s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); + s_expectedLinkBalance = s_expectedLinkBalance + amount; + + emit FundsAdded(id, sender, uint96(amount)); + } + + /** + * @notice removes funding from a canceled upkeep + * @param id upkeep to withdraw funds from + * @param to destination address for sending remaining funds + */ + function withdrawFunds(uint256 id, address to) external validRecipient(to) onlyUpkeepAdmin(id) { + if (s_upkeep[id].maxValidBlocknumber > block.number) revert UpkeepNotCanceled(); + + uint96 minUpkeepSpend = s_storage.minUpkeepSpend; + uint96 amountLeft = s_upkeep[id].balance; + uint96 amountSpent = s_upkeep[id].amountSpent; + + uint96 cancellationFee = 0; + // cancellationFee is supposed to be min(max(minUpkeepSpend - amountSpent,0), amountLeft) + if (amountSpent < minUpkeepSpend) { + cancellationFee = minUpkeepSpend - amountSpent; + if (cancellationFee > amountLeft) { + cancellationFee = amountLeft; + } + } + uint96 amountToWithdraw = amountLeft - cancellationFee; + + s_upkeep[id].balance = 0; + s_ownerLinkBalance = s_ownerLinkBalance + cancellationFee; + + s_expectedLinkBalance = s_expectedLinkBalance - amountToWithdraw; + emit FundsWithdrawn(id, amountToWithdraw, to); + + LINK.transfer(to, amountToWithdraw); + } + + /** + * @notice withdraws LINK funds collected through cancellation fees + */ + function withdrawOwnerFunds() external onlyOwner { + uint96 amount = s_ownerLinkBalance; + + s_expectedLinkBalance = s_expectedLinkBalance - amount; + s_ownerLinkBalance = 0; + + emit OwnerFundsWithdrawn(amount); + LINK.transfer(msg.sender, amount); + } + + /** + * @notice allows the admin of an upkeep to modify gas limit + * @param id upkeep to be change the gas limit for + * @param gasLimit new gas limit for the upkeep + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external override onlyActiveUpkeep(id) onlyUpkeepAdmin(id) { + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + + s_upkeep[id].executeGas = gasLimit; + + emit UpkeepGasLimitSet(id, gasLimit); + } + + /** + * @notice recovers LINK funds improperly transferred to the registry + * @dev In principle this function’s execution cost could exceed block + * gas limit. However, in our anticipated deployment, the number of upkeeps and + * keepers will be low enough to avoid this problem. + */ + function recoverFunds() external onlyOwner { + uint256 total = LINK.balanceOf(address(this)); + LINK.transfer(msg.sender, total - s_expectedLinkBalance); + } + + /** + * @notice withdraws a keeper's payment, callable only by the keeper's payee + * @param from keeper address + * @param to address to send the payment to + */ + function withdrawPayment(address from, address to) external validRecipient(to) { + KeeperInfo memory keeper = s_keeperInfo[from]; + if (keeper.payee != msg.sender) revert OnlyCallableByPayee(); + + s_keeperInfo[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance - keeper.balance; + emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); + + LINK.transfer(to, keeper.balance); + } + + /** + * @notice proposes the safe transfer of a keeper's payee to another address + * @param keeper address of the keeper to transfer payee role + * @param proposed address to nominate for next payeeship + */ + function transferPayeeship(address keeper, address proposed) external { + if (s_keeperInfo[keeper].payee != msg.sender) revert OnlyCallableByPayee(); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedPayee[keeper] != proposed) { + s_proposedPayee[keeper] = proposed; + emit PayeeshipTransferRequested(keeper, msg.sender, proposed); + } + } + + /** + * @notice accepts the safe transfer of payee role for a keeper + * @param keeper address to accept the payee role for + */ + function acceptPayeeship(address keeper) external { + if (s_proposedPayee[keeper] != msg.sender) revert OnlyCallableByProposedPayee(); + address past = s_keeperInfo[keeper].payee; + s_keeperInfo[keeper].payee = msg.sender; + s_proposedPayee[keeper] = ZERO_ADDRESS; + + emit PayeeshipTransferred(keeper, past, msg.sender); + } + + /** + * @notice signals to keepers that they should not perform upkeeps until the + * contract has been unpaused + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice signals to keepers that they can perform upkeeps once again after + * having been paused + */ + function unpause() external onlyOwner { + _unpause(); + } + + // SETTERS + + /** + * @notice updates the configuration of the registry + * @param config registry config fields + */ + function setConfig(Config memory config) public onlyOwner { + if (config.maxPerformGas < s_storage.maxPerformGas) revert GasLimitCanOnlyIncrease(); + s_storage = Storage({ + paymentPremiumPPB: config.paymentPremiumPPB, + flatFeeMicroLink: config.flatFeeMicroLink, + blockCountPerTurn: config.blockCountPerTurn, + checkGasLimit: config.checkGasLimit, + stalenessSeconds: config.stalenessSeconds, + gasCeilingMultiplier: config.gasCeilingMultiplier, + minUpkeepSpend: config.minUpkeepSpend, + maxPerformGas: config.maxPerformGas, + nonce: s_storage.nonce + }); + s_fallbackGasPrice = config.fallbackGasPrice; + s_fallbackLinkPrice = config.fallbackLinkPrice; + s_transcoder = config.transcoder; + s_registrar = config.registrar; + emit ConfigSet(config); + } + + /** + * @notice update the list of keepers allowed to perform upkeep + * @param keepers list of addresses allowed to perform upkeep + * @param payees addresses corresponding to keepers who are allowed to + * move payments which have been accrued + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { + if (keepers.length != payees.length || keepers.length < 2) revert ParameterLengthError(); + for (uint256 i = 0; i < s_keeperList.length; i++) { + address keeper = s_keeperList[i]; + s_keeperInfo[keeper].active = false; + } + for (uint256 i = 0; i < keepers.length; i++) { + address keeper = keepers[i]; + KeeperInfo storage s_keeper = s_keeperInfo[keeper]; + address oldPayee = s_keeper.payee; + address newPayee = payees[i]; + if ( + (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) + ) revert InvalidPayee(); + if (s_keeper.active) revert DuplicateEntry(); + s_keeper.active = true; + if (newPayee != IGNORE_ADDRESS) { + s_keeper.payee = newPayee; + } + } + s_keeperList = keepers; + emit KeepersUpdated(keepers, payees); + } + + // GETTERS + + /** + * @notice read all of the details about an upkeep + */ + function getUpkeep(uint256 id) + external + view + override + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent + ) + { + Upkeep memory reg = s_upkeep[id]; + return ( + reg.target, + reg.executeGas, + s_checkData[id], + reg.balance, + reg.lastKeeper, + reg.admin, + reg.maxValidBlocknumber, + reg.amountSpent + ); + } + + /** + * @notice retrieve active upkeep IDs + * @param startIndex starting index in list + * @param maxCount max count to retrieve (0 = unlimited) + * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one + * should consider keeping the blockheight constant to ensure a wholistic picture of the contract state + */ + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view override returns (uint256[] memory) { + uint256 maxIdx = s_upkeepIDs.length(); + if (startIndex >= maxIdx) revert IndexOutOfRange(); + if (maxCount == 0) { + maxCount = maxIdx - startIndex; + } + uint256[] memory ids = new uint256[](maxCount); + for (uint256 idx = 0; idx < maxCount; idx++) { + ids[idx] = s_upkeepIDs.at(startIndex + idx); + } + return ids; + } + + /** + * @notice read the current info about any keeper address + */ + function getKeeperInfo(address query) + external + view + override + returns ( + address payee, + bool active, + uint96 balance + ) + { + KeeperInfo memory keeper = s_keeperInfo[query]; + return (keeper.payee, keeper.active, keeper.balance); + } + + /** + * @notice read the current state of the registry + */ + function getState() + external + view + override + returns ( + State memory state, + Config memory config, + address[] memory keepers + ) + { + Storage memory store = s_storage; + state.nonce = store.nonce; + state.ownerLinkBalance = s_ownerLinkBalance; + state.expectedLinkBalance = s_expectedLinkBalance; + state.numUpkeeps = s_upkeepIDs.length(); + config.paymentPremiumPPB = store.paymentPremiumPPB; + config.flatFeeMicroLink = store.flatFeeMicroLink; + config.blockCountPerTurn = store.blockCountPerTurn; + config.checkGasLimit = store.checkGasLimit; + config.stalenessSeconds = store.stalenessSeconds; + config.gasCeilingMultiplier = store.gasCeilingMultiplier; + config.minUpkeepSpend = store.minUpkeepSpend; + config.maxPerformGas = store.maxPerformGas; + config.fallbackGasPrice = s_fallbackGasPrice; + config.fallbackLinkPrice = s_fallbackLinkPrice; + config.transcoder = s_transcoder; + config.registrar = s_registrar; + return (state, config, s_keeperList); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { + return getMaxPaymentForGas(s_upkeep[id].executeGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + * @param gasLimit the gas to calculate payment for + */ + function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { + (uint256 gasWei, uint256 linkEth) = _getFeedData(); + uint256 adjustedGasWei = _adjustGasPrice(gasWei, false); + return _calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + } + + /** + * @notice retrieves the migration permission for a peer registry + */ + function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { + return s_peerRegistryMigrationPermission[peer]; + } + + /** + * @notice sets the peer registry migration permission + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { + s_peerRegistryMigrationPermission[peer] = permission; + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external override { + if ( + s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && + s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + if (s_transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); + if (ids.length == 0) revert ArrayHasNoEntries(); + uint256 id; + Upkeep memory upkeep; + uint256 totalBalanceRemaining; + bytes[] memory checkDatas = new bytes[](ids.length); + Upkeep[] memory upkeeps = new Upkeep[](ids.length); + for (uint256 idx = 0; idx < ids.length; idx++) { + id = ids[idx]; + upkeep = s_upkeep[id]; + if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); + upkeeps[idx] = upkeep; + checkDatas[idx] = s_checkData[id]; + totalBalanceRemaining = totalBalanceRemaining + upkeep.balance; + delete s_upkeep[id]; + delete s_checkData[id]; + s_upkeepIDs.remove(id); + emit UpkeepMigrated(id, upkeep.balance, destination); + } + s_expectedLinkBalance = s_expectedLinkBalance - totalBalanceRemaining; + bytes memory encodedUpkeeps = abi.encode(ids, upkeeps, checkDatas); + MigratableKeeperRegistryInterface(destination).receiveUpkeeps( + UpkeepTranscoderInterface(s_transcoder).transcodeUpkeeps( + UpkeepFormat.V1, + MigratableKeeperRegistryInterface(destination).upkeepTranscoderVersion(), + encodedUpkeeps + ) + ); + LINK.transfer(destination, totalBalanceRemaining); + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + UpkeepFormat public constant override upkeepTranscoderVersion = UpkeepFormat.V1; + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external override { + if ( + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + (uint256[] memory ids, Upkeep[] memory upkeeps, bytes[] memory checkDatas) = abi.decode( + encodedUpkeeps, + (uint256[], Upkeep[], bytes[]) + ); + for (uint256 idx = 0; idx < ids.length; idx++) { + _createUpkeep( + ids[idx], + upkeeps[idx].target, + upkeeps[idx].executeGas, + upkeeps[idx].admin, + upkeeps[idx].balance, + checkDatas[idx] + ); + emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); + } + } + + /** + * @notice creates a new upkeep with the given fields + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function _createUpkeep( + uint256 id, + address target, + uint32 gasLimit, + address admin, + uint96 balance, + bytes memory checkData + ) internal whenNotPaused { + if (!target.isContract()) revert NotAContract(); + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + s_upkeep[id] = Upkeep({ + target: target, + executeGas: gasLimit, + balance: balance, + admin: admin, + maxValidBlocknumber: UINT64_MAX, + lastKeeper: ZERO_ADDRESS, + amountSpent: 0 + }); + s_expectedLinkBalance = s_expectedLinkBalance + balance; + s_checkData[id] = checkData; + s_upkeepIDs.add(id); + } + + /** + * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function _getFeedData() private view returns (uint256 gasWei, uint256 linkEth) { + uint32 stalenessSeconds = s_storage.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + linkEth = s_fallbackLinkPrice; + } else { + linkEth = uint256(feedValue); + } + return (gasWei, linkEth); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + */ + function _calculatePaymentAmount( + uint256 gasLimit, + uint256 gasWei, + uint256 linkEth + ) private view returns (uint96 payment) { + uint256 weiForGas = gasWei * (gasLimit + REGISTRY_GAS_OVERHEAD); + uint256 premium = PPB_BASE + s_storage.paymentPremiumPPB; + uint256 total = ((weiForGas * (1e9) * (premium)) / (linkEth)) + (uint256(s_storage.flatFeeMicroLink) * (1e12)); + if (total > LINK_TOTAL_SUPPLY) revert PaymentGreaterThanAllLINK(); + return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available + */ + function _callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= PERFORM_GAS_CUSHION and check for underflow + if lt(g, PERFORM_GAS_CUSHION) { + revert(0, 0) + } + g := sub(g, PERFORM_GAS_CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * keeper and the exact gas required by the Upkeep + */ + function _performUpkeepWithParams(PerformParams memory params) + private + nonReentrant + validUpkeep(params.id) + returns (bool success) + { + Upkeep memory upkeep = s_upkeep[params.id]; + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + uint256 gasUsed = gasleft(); + bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); + success = _callWithExactGas(params.gasLimit, upkeep.target, callData); + gasUsed = gasUsed - gasleft(); + + uint96 payment = _calculatePaymentAmount(gasUsed, params.adjustedGasWei, params.linkEth); + + s_upkeep[params.id].balance = s_upkeep[params.id].balance - payment; + s_upkeep[params.id].amountSpent = s_upkeep[params.id].amountSpent + payment; + s_upkeep[params.id].lastKeeper = params.from; + s_keeperInfo[params.from].balance = s_keeperInfo[params.from].balance + payment; + + emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); + return success; + } + + /** + * @dev ensures all required checks are passed before an upkeep is performed + */ + function _prePerformUpkeep( + Upkeep memory upkeep, + address from, + uint256 maxLinkPayment + ) private view { + if (!s_keeperInfo[from].active) revert OnlyActiveKeepers(); + if (upkeep.balance < maxLinkPayment) revert InsufficientFunds(); + if (upkeep.lastKeeper == from) revert KeepersMustTakeTurns(); + } + + /** + * @dev adjusts the gas price to min(ceiling, tx.gasprice) or just uses the ceiling if tx.gasprice is disabled + */ + function _adjustGasPrice(uint256 gasWei, bool useTxGasPrice) private view returns (uint256 adjustedPrice) { + adjustedPrice = gasWei * s_storage.gasCeilingMultiplier; + if (useTxGasPrice && tx.gasprice < adjustedPrice) { + adjustedPrice = tx.gasprice; + } + } + + /** + * @dev generates a PerformParams struct for use in _performUpkeepWithParams() + */ + function _generatePerformParams( + address from, + uint256 id, + bytes memory performData, + bool useTxGasPrice + ) private view returns (PerformParams memory) { + uint256 gasLimit = s_upkeep[id].executeGas; + (uint256 gasWei, uint256 linkEth) = _getFeedData(); + uint256 adjustedGasWei = _adjustGasPrice(gasWei, useTxGasPrice); + uint96 maxLinkPayment = _calculatePaymentAmount(gasLimit, adjustedGasWei, linkEth); + + return + PerformParams({ + from: from, + id: id, + performData: performData, + maxLinkPayment: maxLinkPayment, + gasLimit: gasLimit, + adjustedGasWei: adjustedGasWei, + linkEth: linkEth + }); + } + + // MODIFIERS + + /** + * @dev ensures a upkeep is valid + */ + modifier validUpkeep(uint256 id) { + if (s_upkeep[id].maxValidBlocknumber <= block.number) revert UpkeepNotActive(); + _; + } + + /** + * @dev Reverts if called by anyone other than the admin of upkeep #id + */ + modifier onlyUpkeepAdmin(uint256 id) { + if (msg.sender != s_upkeep[id].admin) revert OnlyCallableByAdmin(); + _; + } + + /** + * @dev Reverts if called on a cancelled upkeep + */ + modifier onlyActiveUpkeep(uint256 id) { + if (s_upkeep[id].maxValidBlocknumber != UINT64_MAX) revert UpkeepNotActive(); + _; + } + + /** + * @dev ensures that burns don't accidentally happen by sending to the zero + * address + */ + modifier validRecipient(address to) { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + _; + } + + /** + * @dev Reverts if called by anyone other than the contract owner or registrar. + */ + modifier onlyOwnerOrRegistrar() { + if (msg.sender != owner() && msg.sender != s_registrar) revert OnlyCallableByOwnerOrRegistrar(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeepersVRFConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeepersVRFConsumer.sol new file mode 100644 index 0000000..510705e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/KeepersVRFConsumer.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "./KeeperCompatible.sol"; +import "./VRFConsumerBaseV2.sol"; +import "./interfaces/VRFCoordinatorV2Interface.sol"; + +/** + * @title KeepersVRFConsumer + * @notice KeepersVRFConsumer is a Chainlink Keepers compatible contract that also acts as a + * VRF V2 requester and consumer. In particular, a random words request is made when `performUpkeep` + * is called in a cadence provided by the upkeep interval. + */ +contract KeepersVRFConsumer is KeeperCompatibleInterface, VRFConsumerBaseV2 { + // Upkeep interval in seconds. This contract's performUpkeep method will + // be called by the Keepers network roughly every UPKEEP_INTERVAL seconds. + uint256 public immutable UPKEEP_INTERVAL; + + // VRF V2 information, provided upon contract construction. + VRFCoordinatorV2Interface public immutable COORDINATOR; + uint64 public immutable SUBSCRIPTION_ID; + uint16 public immutable REQUEST_CONFIRMATIONS; + bytes32 public immutable KEY_HASH; + + // Contract state, updated in performUpkeep and fulfillRandomWords. + uint256 public s_lastTimeStamp; + uint256 public s_vrfRequestCounter; + uint256 public s_vrfResponseCounter; + + struct RequestRecord { + uint256 requestId; + bool fulfilled; + uint32 callbackGasLimit; + uint256 randomness; + } + mapping(uint256 => RequestRecord) public s_requests; /* request ID */ /* request record */ + + constructor( + address vrfCoordinator, + uint64 subscriptionId, + bytes32 keyHash, + uint16 requestConfirmations, + uint256 upkeepInterval + ) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + SUBSCRIPTION_ID = subscriptionId; + REQUEST_CONFIRMATIONS = requestConfirmations; + KEY_HASH = keyHash; + UPKEEP_INTERVAL = upkeepInterval; + + s_lastTimeStamp = block.timestamp; + s_vrfRequestCounter = 0; + s_vrfResponseCounter = 0; + } + + /** + * @notice Returns true if and only if at least UPKEEP_INTERVAL seconds have elapsed + * since the last upkeep or since construction of the contract. + * @return upkeepNeeded true if and only if at least UPKEEP_INTERVAL seconds have elapsed since the last upkeep or since construction + * of the contract. + */ + function checkUpkeep( + bytes calldata /* checkData */ + ) + external + view + override + returns ( + bool upkeepNeeded, + bytes memory /* performData */ + ) + { + upkeepNeeded = (block.timestamp - s_lastTimeStamp) > UPKEEP_INTERVAL; + } + + /** + * @notice Requests random words from the VRF coordinator if UPKEEP_INTERVAL seconds have elapsed + * since the last upkeep or since construction of the contract. + */ + function performUpkeep( + bytes calldata /* performData */ + ) external override { + if ((block.timestamp - s_lastTimeStamp) > UPKEEP_INTERVAL) { + s_lastTimeStamp = block.timestamp; + + requestRandomWords(); + } + } + + /** + * @notice VRF callback implementation + * @param requestId the VRF V2 request ID, provided at request time. + * @param randomWords the randomness provided by Chainlink VRF. + */ + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { + // Check that the request exists. If not, revert. + RequestRecord memory record = s_requests[requestId]; + require(record.requestId == requestId, "request ID not found in map"); + + // Update the randomness in the record, and increment the response counter. + s_requests[requestId].randomness = randomWords[0]; + s_vrfResponseCounter++; + } + + /** + * @notice Requests random words from Chainlink VRF. + */ + function requestRandomWords() internal { + uint256 requestId = COORDINATOR.requestRandomWords( + KEY_HASH, + SUBSCRIPTION_ID, + REQUEST_CONFIRMATIONS, + 150000, // callback gas limit + 1 // num words + ); + s_requests[requestId] = RequestRecord({ + requestId: requestId, + fulfilled: false, + callbackGasLimit: 150000, + randomness: 0 + }); + s_vrfRequestCounter++; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/PermissionedForwardProxy.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/PermissionedForwardProxy.sol new file mode 100644 index 0000000..9e3cf73 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/PermissionedForwardProxy.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/utils/Address.sol"; +import "./ConfirmedOwner.sol"; + +/** + * @title PermissionedForwardProxy + * @notice This proxy is used to forward calls from sender to target. It maintains + * a permission list to check which sender is allowed to call which target + */ +contract PermissionedForwardProxy is ConfirmedOwner { + using Address for address; + + error PermissionNotSet(); + + event PermissionSet(address indexed sender, address target); + event PermissionRemoved(address indexed sender); + + mapping(address => address) private s_forwardPermissionList; + + constructor() ConfirmedOwner(msg.sender) {} + + /** + * @notice Verifies if msg.sender has permission to forward to target address and then forwards the handler + * @param target address of the contract to forward the handler to + * @param handler bytes to be passed to target in call data + */ + function forward(address target, bytes calldata handler) external { + if (s_forwardPermissionList[msg.sender] != target) { + revert PermissionNotSet(); + } + target.functionCall(handler); + } + + /** + * @notice Adds permission for sender to forward calls to target via this proxy. + * Note that it allows to overwrite an existing permission + * @param sender The address who will use this proxy to forward calls + * @param target The address where sender will be allowed to forward calls + */ + function setPermission(address sender, address target) external onlyOwner { + s_forwardPermissionList[sender] = target; + + emit PermissionSet(sender, target); + } + + /** + * @notice Removes permission for sender to forward calls via this proxy + * @param sender The address who will use this proxy to forward calls + */ + function removePermission(address sender) external onlyOwner { + delete s_forwardPermissionList[sender]; + + emit PermissionRemoved(sender); + } + + /** + * @notice Returns the target address that the sender can use this proxy for + * @param sender The address to fetch the permissioned target for + */ + function getPermission(address sender) external view returns (address) { + return s_forwardPermissionList[sender]; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/SimpleReadAccessController.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/SimpleReadAccessController.sol new file mode 100644 index 0000000..ade1c15 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/SimpleReadAccessController.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./SimpleWriteAccessController.sol"; + +/** + * @title SimpleReadAccessController + * @notice Gives access to: + * - any externally owned account (note that off-chain actors can always read + * any contract storage regardless of on-chain access control measures, so this + * does not weaken the access control while improving usability) + * - accounts explicitly added to an access list + * @dev SimpleReadAccessController is not suitable for access controlling writes + * since it grants any externally owned account access! See + * SimpleWriteAccessController for that. + */ +contract SimpleReadAccessController is SimpleWriteAccessController { + /** + * @notice Returns the access of an address + * @param _user The address to query + */ + function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) { + return super.hasAccess(_user, _calldata) || _user == tx.origin; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/SimpleWriteAccessController.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/SimpleWriteAccessController.sol new file mode 100644 index 0000000..7c770bc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/SimpleWriteAccessController.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./ConfirmedOwner.sol"; +import "./interfaces/AccessControllerInterface.sol"; + +/** + * @title SimpleWriteAccessController + * @notice Gives access to accounts explicitly added to an access list by the + * controller's owner. + * @dev does not make any special permissions for externally, see + * SimpleReadAccessController for that. + */ +contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwner { + bool public checkEnabled; + mapping(address => bool) internal accessList; + + event AddedAccess(address user); + event RemovedAccess(address user); + event CheckAccessEnabled(); + event CheckAccessDisabled(); + + constructor() ConfirmedOwner(msg.sender) { + checkEnabled = true; + } + + /** + * @notice Returns the access of an address + * @param _user The address to query + */ + function hasAccess(address _user, bytes memory) public view virtual override returns (bool) { + return accessList[_user] || !checkEnabled; + } + + /** + * @notice Adds an address to the access list + * @param _user The address to add + */ + function addAccess(address _user) external onlyOwner { + if (!accessList[_user]) { + accessList[_user] = true; + + emit AddedAccess(_user); + } + } + + /** + * @notice Removes an address from the access list + * @param _user The address to remove + */ + function removeAccess(address _user) external onlyOwner { + if (accessList[_user]) { + accessList[_user] = false; + + emit RemovedAccess(_user); + } + } + + /** + * @notice makes the access check enforced + */ + function enableAccessCheck() external onlyOwner { + if (!checkEnabled) { + checkEnabled = true; + + emit CheckAccessEnabled(); + } + } + + /** + * @notice makes the access check unenforced + */ + function disableAccessCheck() external onlyOwner { + if (checkEnabled) { + checkEnabled = false; + + emit CheckAccessDisabled(); + } + } + + /** + * @dev reverts if the caller does not have access + */ + modifier checkAccess() { + require(hasAccess(msg.sender, msg.data), "No access"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/UpkeepFormat.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/UpkeepFormat.sol new file mode 100644 index 0000000..2e09b30 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/UpkeepFormat.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +enum UpkeepFormat { + V1, + V2 +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/UpkeepTranscoder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/UpkeepTranscoder.sol new file mode 100644 index 0000000..462fdf1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/UpkeepTranscoder.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./interfaces/UpkeepTranscoderInterface.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; + +/** + * @notice Transcoder for converting upkeep data from one keeper + * registry version to another + */ +contract UpkeepTranscoder is UpkeepTranscoderInterface, TypeAndVersionInterface { + error InvalidTranscoding(); + + /** + * @notice versions: + * - UpkeepTranscoder 1.0.0: placeholder to allow new formats in the future + */ + string public constant override typeAndVersion = "UpkeepTranscoder 1.0.0"; + + /** + * @notice transcodeUpkeeps transforms upkeep data from the format expected by + * one registry to the format expected by another. It future-proofs migrations + * by allowing keepers team to customize migration paths and set sensible defaults + * when new fields are added + * @param fromVersion struct version the upkeep is migrating from + * @param toVersion struct version the upkeep is migrating to + * @param encodedUpkeeps encoded upkeep data + * @dev this contract & function are simple now, but should evolve as new registries + * and migration paths are added + */ + function transcodeUpkeeps( + UpkeepFormat fromVersion, + UpkeepFormat toVersion, + bytes calldata encodedUpkeeps + ) external view override returns (bytes memory) { + if (fromVersion != toVersion) { + revert InvalidTranscoding(); + } + + return encodedUpkeeps; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRF.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRF.sol new file mode 100644 index 0000000..7e0bbfb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRF.sol @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** **************************************************************************** + * @notice Verification of verifiable-random-function (VRF) proofs, following + * @notice https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @notice See https://eprint.iacr.org/2017/099.pdf for security proofs. + + * @dev Bibliographic references: + + * @dev Goldberg, et al., "Verifiable Random Functions (VRFs)", Internet Draft + * @dev draft-irtf-cfrg-vrf-05, IETF, Aug 11 2019, + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05 + + * @dev Papadopoulos, et al., "Making NSEC5 Practical for DNSSEC", Cryptology + * @dev ePrint Archive, Report 2017/099, https://eprint.iacr.org/2017/099.pdf + * **************************************************************************** + * @dev USAGE + + * @dev The main entry point is randomValueFromVRFProof. See its docstring. + * **************************************************************************** + * @dev PURPOSE + + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is computationally indistinguishable to her from a uniform + * @dev random sample from the output space. + + * @dev The purpose of this contract is to perform that verification. + * **************************************************************************** + * @dev DESIGN NOTES + + * @dev The VRF algorithm verified here satisfies the full uniqueness, full + * @dev collision resistance, and full pseudo-randomness security properties. + * @dev See "SECURITY PROPERTIES" below, and + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-3 + + * @dev An elliptic curve point is generally represented in the solidity code + * @dev as a uint256[2], corresponding to its affine coordinates in + * @dev GF(FIELD_SIZE). + + * @dev For the sake of efficiency, this implementation deviates from the spec + * @dev in some minor ways: + + * @dev - Keccak hash rather than the SHA256 hash recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev Keccak costs much less gas on the EVM, and provides similar security. + + * @dev - Secp256k1 curve instead of the P-256 or ED25519 curves recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev For curve-point multiplication, it's much cheaper to abuse ECRECOVER + + * @dev - hashToCurve recursively hashes until it finds a curve x-ordinate. On + * @dev the EVM, this is slightly more efficient than the recommendation in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + * @dev step 5, to concatenate with a nonce then hash, and rehash with the + * @dev nonce updated until a valid x-ordinate is found. + + * @dev - hashToCurve does not include a cipher version string or the byte 0x1 + * @dev in the hash message, as recommended in step 5.B of the draft + * @dev standard. They are unnecessary here because no variation in the + * @dev cipher suite is allowed. + + * @dev - Similarly, the hash input in scalarFromCurvePoints does not include a + * @dev commitment to the cipher suite, either, which differs from step 2 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + * @dev . Also, the hash input is the concatenation of the uncompressed + * @dev points, not the compressed points as recommended in step 3. + + * @dev - In the calculation of the challenge value "c", the "u" value (i.e. + * @dev the value computed by Reggie as the nonce times the secp256k1 + * @dev generator point, see steps 5 and 7 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @dev ) is replaced by its ethereum address, i.e. the lower 160 bits of the + * @dev keccak hash of the original u. This is because we only verify the + * @dev calculation of u up to its address, by abusing ECRECOVER. + * **************************************************************************** + * @dev SECURITY PROPERTIES + + * @dev Here are the security properties for this VRF: + + * @dev Full uniqueness: For any seed and valid VRF public key, there is + * @dev exactly one VRF output which can be proved to come from that seed, in + * @dev the sense that the proof will pass verifyVRFProof. + + * @dev Full collision resistance: It's cryptographically infeasible to find + * @dev two seeds with same VRF output from a fixed, valid VRF key + + * @dev Full pseudorandomness: Absent the proofs that the VRF outputs are + * @dev derived from a given seed, the outputs are computationally + * @dev indistinguishable from randomness. + + * @dev https://eprint.iacr.org/2017/099.pdf, Appendix B contains the proofs + * @dev for these properties. + + * @dev For secp256k1, the key validation described in section + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.6 + * @dev is unnecessary, because secp256k1 has cofactor 1, and the + * @dev representation of the public key used here (affine x- and y-ordinates + * @dev of the secp256k1 point on the standard y^2=x^3+7 curve) cannot refer to + * @dev the point at infinity. + * **************************************************************************** + * @dev OTHER SECURITY CONSIDERATIONS + * + * @dev The seed input to the VRF could in principle force an arbitrary amount + * @dev of work in hashToCurve, by requiring extra rounds of hashing and + * @dev checking whether that's yielded the x ordinate of a secp256k1 point. + * @dev However, under the Random Oracle Model the probability of choosing a + * @dev point which forces n extra rounds in hashToCurve is 2⁻ⁿ. The base cost + * @dev for calling hashToCurve is about 25,000 gas, and each round of checking + * @dev for a valid x ordinate costs about 15,555 gas, so to find a seed for + * @dev which hashToCurve would cost more than 2,017,000 gas, one would have to + * @dev try, in expectation, about 2¹²⁸ seeds, which is infeasible for any + * @dev foreseeable computational resources. (25,000 + 128 * 15,555 < 2,017,000.) + + * @dev Since the gas block limit for the Ethereum main net is 10,000,000 gas, + * @dev this means it is infeasible for an adversary to prevent correct + * @dev operation of this contract by choosing an adverse seed. + + * @dev (See TestMeasureHashToCurveGasCost for verification of the gas cost for + * @dev hashToCurve.) + + * @dev It may be possible to make a secure constant-time hashToCurve function. + * @dev See notes in hashToCurve docstring. +*/ +contract VRF { + // See https://www.secg.org/sec2-v2.pdf, section 2.4.1, for these constants. + // Number of points in Secp256k1 + uint256 private constant GROUP_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + // Prime characteristic of the galois field over which Secp256k1 is defined + uint256 private constant FIELD_SIZE = + // solium-disable-next-line indentation + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; + uint256 private constant WORD_LENGTH_BYTES = 0x20; + + // (base^exponent) % FIELD_SIZE + // Cribbed from https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 + function bigModExp(uint256 base, uint256 exponent) internal view returns (uint256 exponentiation) { + uint256 callResult; + uint256[6] memory bigModExpContractInputs; + bigModExpContractInputs[0] = WORD_LENGTH_BYTES; // Length of base + bigModExpContractInputs[1] = WORD_LENGTH_BYTES; // Length of exponent + bigModExpContractInputs[2] = WORD_LENGTH_BYTES; // Length of modulus + bigModExpContractInputs[3] = base; + bigModExpContractInputs[4] = exponent; + bigModExpContractInputs[5] = FIELD_SIZE; + uint256[1] memory output; + assembly { + // solhint-disable-line no-inline-assembly + callResult := staticcall( + not(0), // Gas cost: no limit + 0x05, // Bigmodexp contract address + bigModExpContractInputs, + 0xc0, // Length of input segment: 6*0x20-bytes + output, + 0x20 // Length of output segment + ) + } + if (callResult == 0) { + revert("bigModExp failure!"); + } + return output[0]; + } + + // Let q=FIELD_SIZE. q % 4 = 3, ∴ x≡r^2 mod q ⇒ x^SQRT_POWER≡±r mod q. See + // https://en.wikipedia.org/wiki/Modular_square_root#Prime_or_prime_power_modulus + uint256 private constant SQRT_POWER = (FIELD_SIZE + 1) >> 2; + + // Computes a s.t. a^2 = x in the field. Assumes a exists + function squareRoot(uint256 x) internal view returns (uint256) { + return bigModExp(x, SQRT_POWER); + } + + // The value of y^2 given that (x,y) is on secp256k1. + function ySquared(uint256 x) internal pure returns (uint256) { + // Curve is y^2=x^3+7. See section 2.4.1 of https://www.secg.org/sec2-v2.pdf + uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE); + return addmod(xCubed, 7, FIELD_SIZE); + } + + // True iff p is on secp256k1 + function isOnCurve(uint256[2] memory p) internal pure returns (bool) { + // Section 2.3.6. in https://www.secg.org/sec1-v2.pdf + // requires each ordinate to be in [0, ..., FIELD_SIZE-1] + require(p[0] < FIELD_SIZE, "invalid x-ordinate"); + require(p[1] < FIELD_SIZE, "invalid y-ordinate"); + return ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE); + } + + // Hash x uniformly into {0, ..., FIELD_SIZE-1}. + function fieldHash(bytes memory b) internal pure returns (uint256 x_) { + x_ = uint256(keccak256(b)); + // Rejecting if x >= FIELD_SIZE corresponds to step 2.1 in section 2.3.4 of + // http://www.secg.org/sec1-v2.pdf , which is part of the definition of + // string_to_point in the IETF draft + while (x_ >= FIELD_SIZE) { + x_ = uint256(keccak256(abi.encodePacked(x_))); + } + } + + // Hash b to a random point which hopefully lies on secp256k1. The y ordinate + // is always even, due to + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + // step 5.C, which references arbitrary_string_to_point, defined in + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 as + // returning the point with given x ordinate, and even y ordinate. + function newCandidateSecp256k1Point(bytes memory b) internal view returns (uint256[2] memory p) { + unchecked { + p[0] = fieldHash(b); + p[1] = squareRoot(ySquared(p[0])); + if (p[1] % 2 == 1) { + // Note that 0 <= p[1] < FIELD_SIZE + // so this cannot wrap, we use unchecked to save gas. + p[1] = FIELD_SIZE - p[1]; + } + } + } + + // Domain-separation tag for initial hash in hashToCurve. Corresponds to + // vrf.go/hashToCurveHashPrefix + uint256 internal constant HASH_TO_CURVE_HASH_PREFIX = 1; + + // Cryptographic hash function onto the curve. + // + // Corresponds to algorithm in section 5.4.1.1 of the draft standard. (But see + // DESIGN NOTES above for slight differences.) + // + // TODO(alx): Implement a bounded-computation hash-to-curve, as described in + // "Construction of Rational Points on Elliptic Curves over Finite Fields" + // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.831.5299&rep=rep1&type=pdf + // and suggested by + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-01#section-5.2.2 + // (Though we can't used exactly that because secp256k1's j-invariant is 0.) + // + // This would greatly simplify the analysis in "OTHER SECURITY CONSIDERATIONS" + // https://www.pivotaltracker.com/story/show/171120900 + function hashToCurve(uint256[2] memory pk, uint256 input) internal view returns (uint256[2] memory rv) { + rv = newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, pk, input)); + while (!isOnCurve(rv)) { + rv = newCandidateSecp256k1Point(abi.encodePacked(rv[0])); + } + } + + /** ********************************************************************* + * @notice Check that product==scalar*multiplicand + * + * @dev Based on Vitalik Buterin's idea in ethresear.ch post cited below. + * + * @param multiplicand: secp256k1 point + * @param scalar: non-zero GF(GROUP_ORDER) scalar + * @param product: secp256k1 expected to be multiplier * multiplicand + * @return verifies true iff product==scalar*multiplicand, with cryptographically high probability + */ + function ecmulVerify( + uint256[2] memory multiplicand, + uint256 scalar, + uint256[2] memory product + ) internal pure returns (bool verifies) { + require(scalar != 0, "zero scalar"); // Rules out an ecrecover failure case + uint256 x = multiplicand[0]; // x ordinate of multiplicand + uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28; // parity of y ordinate + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // Point corresponding to address ecrecover(0, v, x, s=scalar*x) is + // (x⁻¹ mod GROUP_ORDER) * (scalar * x * multiplicand - 0 * g), i.e. + // scalar*multiplicand. See https://crypto.stackexchange.com/a/18106 + bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER)); + address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX); + // Explicit conversion to address takes bottom 160 bits + address expected = address(uint160(uint256(keccak256(abi.encodePacked(product))))); + return (actual == expected); + } + + // Returns x1/z1-x2/z2=(x1z2-x2z1)/(z1z2) in projective coordinates on P¹(𝔽ₙ) + function projectiveSub( + uint256 x1, + uint256 z1, + uint256 x2, + uint256 z2 + ) internal pure returns (uint256 x3, uint256 z3) { + unchecked { + uint256 num1 = mulmod(z2, x1, FIELD_SIZE); + // Note this cannot wrap since x2 is a point in [0, FIELD_SIZE-1] + // we use unchecked to save gas. + uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); + (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + } + + // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) + function projectiveMul( + uint256 x1, + uint256 z1, + uint256 x2, + uint256 z2 + ) internal pure returns (uint256 x3, uint256 z3) { + (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + + /** ************************************************************************** + @notice Computes elliptic-curve sum, in projective co-ordinates + + @dev Using projective coordinates avoids costly divisions + + @dev To use this with p and q in affine coordinates, call + @dev projectiveECAdd(px, py, qx, qy). This will return + @dev the addition of (px, py, 1) and (qx, qy, 1), in the + @dev secp256k1 group. + + @dev This can be used to calculate the z which is the inverse to zInv + @dev in isValidVRFOutput. But consider using a faster + @dev re-implementation such as ProjectiveECAdd in the golang vrf package. + + @dev This function assumes [px,py,1],[qx,qy,1] are valid projective + coordinates of secp256k1 points. That is safe in this contract, + because this method is only used by linearCombination, which checks + points are on the curve via ecrecover. + ************************************************************************** + @param px The first affine coordinate of the first summand + @param py The second affine coordinate of the first summand + @param qx The first affine coordinate of the second summand + @param qy The second affine coordinate of the second summand + + (px,py) and (qx,qy) must be distinct, valid secp256k1 points. + ************************************************************************** + Return values are projective coordinates of [px,py,1]+[qx,qy,1] as points + on secp256k1, in P²(𝔽ₙ) + @return sx + @return sy + @return sz + */ + function projectiveECAdd( + uint256 px, + uint256 py, + uint256 qx, + uint256 qy + ) + internal + pure + returns ( + uint256 sx, + uint256 sy, + uint256 sz + ) + { + unchecked { + // See "Group law for E/K : y^2 = x^3 + ax + b", in section 3.1.2, p. 80, + // "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes and Vanstone + // We take the equations there for (sx,sy), and homogenize them to + // projective coordinates. That way, no inverses are required, here, and we + // only need the one inverse in affineECAdd. + + // We only need the "point addition" equations from Hankerson et al. Can + // skip the "point doubling" equations because p1 == p2 is cryptographically + // impossible, and required not to be the case in linearCombination. + + // Add extra "projective coordinate" to the two points + (uint256 z1, uint256 z2) = (1, 1); + + // (lx, lz) = (qy-py)/(qx-px), i.e., gradient of secant line. + // Cannot wrap since px and py are in [0, FIELD_SIZE-1] + uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE); + uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE); + + uint256 dx; // Accumulates denominator from sx calculation + // sx=((qy-py)/(qx-px))^2-px-qx + (sx, dx) = projectiveMul(lx, lz, lx, lz); // ((qy-py)/(qx-px))^2 + (sx, dx) = projectiveSub(sx, dx, px, z1); // ((qy-py)/(qx-px))^2-px + (sx, dx) = projectiveSub(sx, dx, qx, z2); // ((qy-py)/(qx-px))^2-px-qx + + uint256 dy; // Accumulates denominator from sy calculation + // sy=((qy-py)/(qx-px))(px-sx)-py + (sy, dy) = projectiveSub(px, z1, sx, dx); // px-sx + (sy, dy) = projectiveMul(sy, dy, lx, lz); // ((qy-py)/(qx-px))(px-sx) + (sy, dy) = projectiveSub(sy, dy, py, z1); // ((qy-py)/(qx-px))(px-sx)-py + + if (dx != dy) { + // Cross-multiply to put everything over a common denominator + sx = mulmod(sx, dy, FIELD_SIZE); + sy = mulmod(sy, dx, FIELD_SIZE); + sz = mulmod(dx, dy, FIELD_SIZE); + } else { + // Already over a common denominator, use that for z ordinate + sz = dx; + } + } + } + + // p1+p2, as affine points on secp256k1. + // + // invZ must be the inverse of the z returned by projectiveECAdd(p1, p2). + // It is computed off-chain to save gas. + // + // p1 and p2 must be distinct, because projectiveECAdd doesn't handle + // point doubling. + function affineECAdd( + uint256[2] memory p1, + uint256[2] memory p2, + uint256 invZ + ) internal pure returns (uint256[2] memory) { + uint256 x; + uint256 y; + uint256 z; + (x, y, z) = projectiveECAdd(p1[0], p1[1], p2[0], p2[1]); + require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z"); + // Clear the z ordinate of the projective representation by dividing through + // by it, to obtain the affine representation + return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)]; + } + + // True iff address(c*p+s*g) == lcWitness, where g is generator. (With + // cryptographically high probability.) + function verifyLinearCombinationWithGenerator( + uint256 c, + uint256[2] memory p, + uint256 s, + address lcWitness + ) internal pure returns (bool) { + // Rule out ecrecover failure modes which return address 0. + unchecked { + require(lcWitness != address(0), "bad witness"); + uint8 v = (p[1] % 2 == 0) ? 27 : 28; // parity of y-ordinate of p + // Note this cannot wrap (X - Y % X), but we use unchecked to save + // gas. + bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER)); // -s*p[0] + bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER)); // c*p[0] + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // The point corresponding to the address returned by + // ecrecover(-s*p[0],v,p[0],c*p[0]) is + // (p[0]⁻¹ mod GROUP_ORDER)*(c*p[0]-(-s)*p[0]*g)=c*p+s*g. + // See https://crypto.stackexchange.com/a/18106 + // https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v + address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature); + return computed == lcWitness; + } + } + + // c*p1 + s*p2. Requires cp1Witness=c*p1 and sp2Witness=s*p2. Also + // requires cp1Witness != sp2Witness (which is fine for this application, + // since it is cryptographically impossible for them to be equal. In the + // (cryptographically impossible) case that a prover accidentally derives + // a proof with equal c*p1 and s*p2, they should retry with a different + // proof nonce.) Assumes that all points are on secp256k1 + // (which is checked in verifyVRFProof below.) + function linearCombination( + uint256 c, + uint256[2] memory p1, + uint256[2] memory cp1Witness, + uint256 s, + uint256[2] memory p2, + uint256[2] memory sp2Witness, + uint256 zInv + ) internal pure returns (uint256[2] memory) { + unchecked { + // Note we are relying on the wrap around here + require((cp1Witness[0] % FIELD_SIZE) != (sp2Witness[0] % FIELD_SIZE), "points in sum must be distinct"); + require(ecmulVerify(p1, c, cp1Witness), "First mul check failed"); + require(ecmulVerify(p2, s, sp2Witness), "Second mul check failed"); + return affineECAdd(cp1Witness, sp2Witness, zInv); + } + } + + // Domain-separation tag for the hash taken in scalarFromCurvePoints. + // Corresponds to scalarFromCurveHashPrefix in vrf.go + uint256 internal constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2; + + // Pseudo-random number from inputs. Matches vrf.go/scalarFromCurvePoints, and + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + // The draft calls (in step 7, via the definition of string_to_int, in + // https://datatracker.ietf.org/doc/html/rfc8017#section-4.2 ) for taking the + // first hash without checking that it corresponds to a number less than the + // group order, which will lead to a slight bias in the sample. + // + // TODO(alx): We could save a bit of gas by following the standard here and + // using the compressed representation of the points, if we collated the y + // parities into a single bytes32. + // https://www.pivotaltracker.com/story/show/171120588 + function scalarFromCurvePoints( + uint256[2] memory hash, + uint256[2] memory pk, + uint256[2] memory gamma, + address uWitness, + uint256[2] memory v + ) internal pure returns (uint256 s) { + return uint256(keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, hash, pk, gamma, v, uWitness))); + } + + // True if (gamma, c, s) is a correctly constructed randomness proof from pk + // and seed. zInv must be the inverse of the third ordinate from + // projectiveECAdd applied to cGammaWitness and sHashWitness. Corresponds to + // section 5.3 of the IETF draft. + // + // TODO(alx): Since I'm only using pk in the ecrecover call, I could only pass + // the x ordinate, and the parity of the y ordinate in the top bit of uWitness + // (which I could make a uint256 without using any extra space.) Would save + // about 2000 gas. https://www.pivotaltracker.com/story/show/170828567 + function verifyVRFProof( + uint256[2] memory pk, + uint256[2] memory gamma, + uint256 c, + uint256 s, + uint256 seed, + address uWitness, + uint256[2] memory cGammaWitness, + uint256[2] memory sHashWitness, + uint256 zInv + ) internal view { + unchecked { + require(isOnCurve(pk), "public key is not on curve"); + require(isOnCurve(gamma), "gamma is not on curve"); + require(isOnCurve(cGammaWitness), "cGammaWitness is not on curve"); + require(isOnCurve(sHashWitness), "sHashWitness is not on curve"); + // Step 5. of IETF draft section 5.3 (pk corresponds to 5.3's Y, and here + // we use the address of u instead of u itself. Also, here we add the + // terms instead of taking the difference, and in the proof construction in + // vrf.GenerateProof, we correspondingly take the difference instead of + // taking the sum as they do in step 7 of section 5.1.) + require(verifyLinearCombinationWithGenerator(c, pk, s, uWitness), "addr(c*pk+s*g)!=_uWitness"); + // Step 4. of IETF draft section 5.3 (pk corresponds to Y, seed to alpha_string) + uint256[2] memory hash = hashToCurve(pk, seed); + // Step 6. of IETF draft section 5.3, but see note for step 5 about +/- terms + uint256[2] memory v = linearCombination(c, gamma, cGammaWitness, s, hash, sHashWitness, zInv); + // Steps 7. and 8. of IETF draft section 5.3 + uint256 derivedC = scalarFromCurvePoints(hash, pk, gamma, uWitness, v); + require(c == derivedC, "invalid proof"); + } + } + + // Domain-separation tag for the hash used as the final VRF output. + // Corresponds to vrfRandomOutputHashPrefix in vrf.go + uint256 internal constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3; + + struct Proof { + uint256[2] pk; + uint256[2] gamma; + uint256 c; + uint256 s; + uint256 seed; + address uWitness; + uint256[2] cGammaWitness; + uint256[2] sHashWitness; + uint256 zInv; + } + + /* *************************************************************************** + * @notice Returns proof's output, if proof is valid. Otherwise reverts + + * @param proof vrf proof components + * @param seed seed used to generate the vrf output + * + * Throws if proof is invalid, otherwise: + * @return output i.e., the random output implied by the proof + * *************************************************************************** + */ + function randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) { + verifyVRFProof( + proof.pk, + proof.gamma, + proof.c, + proof.s, + seed, + proof.uWitness, + proof.cGammaWitness, + proof.sHashWitness, + proof.zInv + ); + output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma))); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFConsumerBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFConsumerBase.sol new file mode 100644 index 0000000..15dc7f8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFConsumerBase.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./interfaces/LinkTokenInterface.sol"; + +import "./VRFRequestIDBase.sol"; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constructor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator, _link) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash), and have told you the minimum LINK + * @dev price for VRF service. Make sure your contract has sufficient LINK, and + * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you + * @dev want to generate randomness from. + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomness method. + * + * @dev The randomness argument to fulfillRandomness is the actual random value + * @dev generated from your seed. + * + * @dev The requestId argument is generated from the keyHash and the seed by + * @dev makeRequestId(keyHash, seed). If your contract could have concurrent + * @dev requests open, you can use the requestId to track which seed is + * @dev associated with which randomness. See VRFRequestIDBase.sol for more + * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously.) + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. (Which is critical to making unpredictable randomness! See the + * @dev next section.) + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the ultimate input to the VRF is mixed with the block hash of the + * @dev block in which the request is made, user-provided seeds have no impact + * @dev on its economic security properties. They are only included for API + * @dev compatability with previous versions of this contract. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. + */ +abstract contract VRFConsumerBase is VRFRequestIDBase { + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBase expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomness the VRF output + */ + function fulfillRandomness(bytes32 requestId, uint256 randomness) internal virtual; + + /** + * @dev In order to keep backwards compatibility we have kept the user + * seed field around. We remove the use of it because given that the blockhash + * enters later, it overrides whatever randomness the used seed provides. + * Given that it adds no security, and can easily lead to misunderstandings, + * we have removed it from usage and can now provide a simpler API. + */ + uint256 private constant USER_SEED_PLACEHOLDER = 0; + + /** + * @notice requestRandomness initiates a request for VRF output given _seed + * + * @dev The fulfillRandomness method receives the output, once it's provided + * @dev by the Oracle, and verified by the vrfCoordinator. + * + * @dev The _keyHash must already be registered with the VRFCoordinator, and + * @dev the _fee must exceed the fee specified during registration of the + * @dev _keyHash. + * + * @dev The _seed parameter is vestigial, and is kept only for API + * @dev compatibility with older versions. It can't *hurt* to mix in some of + * @dev your own randomness, here, but it's not necessary because the VRF + * @dev oracle will mix the hash of the block containing your request into the + * @dev VRF seed it ultimately uses. + * + * @param _keyHash ID of public key against which randomness is generated + * @param _fee The amount of LINK to send with the request + * + * @return requestId unique ID for this request + * + * @dev The returned requestId can be used to distinguish responses to + * @dev concurrent requests. It is passed as the first argument to + * @dev fulfillRandomness. + */ + function requestRandomness(bytes32 _keyHash, uint256 _fee) internal returns (bytes32 requestId) { + LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); + // This is the seed passed to VRFCoordinator. The oracle will mix this with + // the hash of the block containing this request to obtain the seed/input + // which is finally passed to the VRF cryptographic machinery. + uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); + // nonces[_keyHash] must stay in sync with + // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above + // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). + // This provides protection against the user repeating their input seed, + // which would result in a predictable/duplicate output, if multiple such + // requests appeared in the same block. + nonces[_keyHash] = nonces[_keyHash] + 1; + return makeRequestId(_keyHash, vRFSeed); + } + + LinkTokenInterface internal immutable LINK; + address private immutable vrfCoordinator; + + // Nonces for each VRF key from which randomness has been requested. + // + // Must stay in sync with VRFCoordinator[_keyHash][this] + mapping(bytes32 => uint256) /* keyHash */ /* nonce */ + private nonces; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + * @param _link address of LINK token contract + * + * @dev https://docs.chain.link/docs/link-token-contracts + */ + constructor(address _vrfCoordinator, address _link) { + vrfCoordinator = _vrfCoordinator; + LINK = LinkTokenInterface(_link); + } + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external { + require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); + fulfillRandomness(requestId, randomness); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFConsumerBaseV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFConsumerBaseV2.sol new file mode 100644 index 0000000..e023373 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFConsumerBaseV2.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. It ensures 2 things: + * @dev 1. The fulfillment came from the VRFCoordinator + * @dev 2. The consumer contract implements fulfillRandomWords. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constructor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash). Create subscription, fund it + * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface + * @dev subscription management functions). + * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations, + * @dev callbackGasLimit, numWords), + * @dev see (VRFCoordinatorInterface for a description of the arguments). + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomWords method. + * + * @dev The randomness argument to fulfillRandomWords is a set of random words + * @dev generated from your requestId and the blockHash of the request. + * + * @dev If your contract could have concurrent requests open, you can use the + * @dev requestId returned from requestRandomWords to track which response is associated + * @dev with which randomness request. + * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously. + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. It is for this reason that + * @dev that you can signal to an oracle you'd like them to wait longer before + * @dev responding to the request (however this is not enforced in the contract + * @dev and so remains effective only in the case of unmodified oracle software). + */ +abstract contract VRFConsumerBaseV2 { + error OnlyCoordinatorCanFulfill(address have, address want); + address private immutable vrfCoordinator; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + */ + constructor(address _vrfCoordinator) { + vrfCoordinator = _vrfCoordinator; + } + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomWords the VRF output expanded to the requested number of words + */ + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual; + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external { + if (msg.sender != vrfCoordinator) { + revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator); + } + fulfillRandomWords(requestId, randomWords); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFCoordinatorV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFCoordinatorV2.sol new file mode 100644 index 0000000..1466d8b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFCoordinatorV2.sol @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/BlockhashStoreInterface.sol"; +import "./interfaces/AggregatorV3Interface.sol"; +import "./interfaces/VRFCoordinatorV2Interface.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./interfaces/ERC677ReceiverInterface.sol"; +import "./VRF.sol"; +import "./ConfirmedOwner.sol"; +import "./VRFConsumerBaseV2.sol"; + +contract VRFCoordinatorV2 is + VRF, + ConfirmedOwner, + TypeAndVersionInterface, + VRFCoordinatorV2Interface, + ERC677ReceiverInterface +{ + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + BlockhashStoreInterface public immutable BLOCKHASH_STORE; + + // We need to maintain a list of consuming addresses. + // This bound ensures we are able to loop over them as needed. + // Should a user require more consumers, they can use multiple subscriptions. + uint16 public constant MAX_CONSUMERS = 100; + error TooManyConsumers(); + error InsufficientBalance(); + error InvalidConsumer(uint64 subId, address consumer); + error InvalidSubscription(); + error OnlyCallableFromLink(); + error InvalidCalldata(); + error MustBeSubOwner(address owner); + error PendingRequestExists(); + error MustBeRequestedOwner(address proposedOwner); + error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen + event FundsRecovered(address to, uint256 amount); + // We use the subscription struct (1 word) + // at fulfillment time. + struct Subscription { + // There are only 1e9*1e18 = 1e27 juels in existence, so the balance can fit in uint96 (2^96 ~ 7e28) + uint96 balance; // Common link balance used for all consumer requests. + uint64 reqCount; // For fee tiers + } + // We use the config for the mgmt APIs + struct SubscriptionConfig { + address owner; // Owner can fund/withdraw/cancel the sub. + address requestedOwner; // For safely transferring sub ownership. + // Maintains the list of keys in s_consumers. + // We do this for 2 reasons: + // 1. To be able to clean up all keys from s_consumers when canceling a subscription. + // 2. To be able to return the list of all consumers in getSubscription. + // Note that we need the s_consumers map to be able to directly check if a + // consumer is valid without reading all the consumers from storage. + address[] consumers; + } + // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. + mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subId */ /* nonce */ + private s_consumers; + mapping(uint64 => SubscriptionConfig) /* subId */ /* subscriptionConfig */ + private s_subscriptionConfigs; + mapping(uint64 => Subscription) /* subId */ /* subscription */ + private s_subscriptions; + // We make the sub count public so that its possible to + // get all the current subscriptions via getSubscription. + uint64 private s_currentSubId; + // s_totalBalance tracks the total link sent to/from + // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. + // A discrepancy with this contract's link balance indicates someone + // sent tokens using transfer and so we may need to use recoverFunds. + uint96 private s_totalBalance; + event SubscriptionCreated(uint64 indexed subId, address owner); + event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); + event SubscriptionConsumerAdded(uint64 indexed subId, address consumer); + event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer); + event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); + event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to); + event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to); + + // Set this maximum to 200 to give us a 56 block window to fulfill + // the request before requiring the block hash feeder. + uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; + uint32 public constant MAX_NUM_WORDS = 500; + // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) + // and some arithmetic operations. + uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; + error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); + error GasLimitTooBig(uint32 have, uint32 want); + error NumWordsTooBig(uint32 have, uint32 want); + error ProvingKeyAlreadyRegistered(bytes32 keyHash); + error NoSuchProvingKey(bytes32 keyHash); + error InvalidLinkWeiPrice(int256 linkWei); + error InsufficientGasForConsumer(uint256 have, uint256 want); + error NoCorrespondingRequest(); + error IncorrectCommitment(); + error BlockhashNotInStore(uint256 blockNum); + error PaymentTooLarge(); + error Reentrant(); + struct RequestCommitment { + uint64 blockNum; + uint64 subId; + uint32 callbackGasLimit; + uint32 numWords; + address sender; + } + mapping(bytes32 => address) /* keyHash */ /* oracle */ + private s_provingKeys; + bytes32[] private s_provingKeyHashes; + mapping(address => uint96) /* oracle */ /* LINK balance */ + private s_withdrawableTokens; + mapping(uint256 => bytes32) /* requestID */ /* commitment */ + private s_requestCommitments; + event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); + event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle); + event RandomWordsRequested( + bytes32 indexed keyHash, + uint256 requestId, + uint256 preSeed, + uint64 indexed subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + address indexed sender + ); + event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); + + struct Config { + uint16 minimumRequestConfirmations; + uint32 maxGasLimit; + // Reentrancy protection. + bool reentrancyLock; + // stalenessSeconds is how long before we consider the feed price to be stale + // and fallback to fallbackWeiPerUnitLink. + uint32 stalenessSeconds; + // Gas to cover oracle payment after we calculate the payment. + // We make it configurable in case those operations are repriced. + uint32 gasAfterPaymentCalculation; + } + int256 private s_fallbackWeiPerUnitLink; + Config private s_config; + FeeConfig private s_feeConfig; + struct FeeConfig { + // Flat fee charged per fulfillment in millionths of link + // So fee range is [0, 2^32/10^6]. + uint32 fulfillmentFlatFeeLinkPPMTier1; + uint32 fulfillmentFlatFeeLinkPPMTier2; + uint32 fulfillmentFlatFeeLinkPPMTier3; + uint32 fulfillmentFlatFeeLinkPPMTier4; + uint32 fulfillmentFlatFeeLinkPPMTier5; + uint24 reqsForTier2; + uint24 reqsForTier3; + uint24 reqsForTier4; + uint24 reqsForTier5; + } + event ConfigSet( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + FeeConfig feeConfig + ); + + constructor( + address link, + address blockhashStore, + address linkEthFeed + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + BLOCKHASH_STORE = BlockhashStoreInterface(blockhashStore); + } + + /** + * @notice Registers a proving key to an oracle. + * @param oracle address of the oracle + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + if (s_provingKeys[kh] != address(0)) { + revert ProvingKeyAlreadyRegistered(kh); + } + s_provingKeys[kh] = oracle; + s_provingKeyHashes.push(kh); + emit ProvingKeyRegistered(kh, oracle); + } + + /** + * @notice Deregisters a proving key to an oracle. + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + address oracle = s_provingKeys[kh]; + if (oracle == address(0)) { + revert NoSuchProvingKey(kh); + } + delete s_provingKeys[kh]; + for (uint256 i = 0; i < s_provingKeyHashes.length; i++) { + if (s_provingKeyHashes[i] == kh) { + bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1]; + // Copy last element and overwrite kh to be deleted with it + s_provingKeyHashes[i] = last; + s_provingKeyHashes.pop(); + } + } + emit ProvingKeyDeregistered(kh, oracle); + } + + /** + * @notice Returns the proving key hash key associated with this public key + * @param publicKey the key to return the hash of + */ + function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { + return keccak256(abi.encode(publicKey)); + } + + /** + * @notice Sets the configuration of the vrfv2 coordinator + * @param minimumRequestConfirmations global min for request confirmations + * @param maxGasLimit global max for request gas limit + * @param stalenessSeconds if the eth/link feed is more stale then this, use the fallback price + * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement + * @param fallbackWeiPerUnitLink fallback eth/link price in the case of a stale feed + * @param feeConfig fee tier configuration + */ + function setConfig( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + FeeConfig memory feeConfig + ) external onlyOwner { + if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { + revert InvalidRequestConfirmations( + minimumRequestConfirmations, + minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + if (fallbackWeiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink); + } + s_config = Config({ + minimumRequestConfirmations: minimumRequestConfirmations, + maxGasLimit: maxGasLimit, + stalenessSeconds: stalenessSeconds, + gasAfterPaymentCalculation: gasAfterPaymentCalculation, + reentrancyLock: false + }); + s_feeConfig = feeConfig; + s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; + emit ConfigSet( + minimumRequestConfirmations, + maxGasLimit, + stalenessSeconds, + gasAfterPaymentCalculation, + fallbackWeiPerUnitLink, + s_feeConfig + ); + } + + function getConfig() + external + view + returns ( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation + ) + { + return ( + s_config.minimumRequestConfirmations, + s_config.maxGasLimit, + s_config.stalenessSeconds, + s_config.gasAfterPaymentCalculation + ); + } + + function getFeeConfig() + external + view + returns ( + uint32 fulfillmentFlatFeeLinkPPMTier1, + uint32 fulfillmentFlatFeeLinkPPMTier2, + uint32 fulfillmentFlatFeeLinkPPMTier3, + uint32 fulfillmentFlatFeeLinkPPMTier4, + uint32 fulfillmentFlatFeeLinkPPMTier5, + uint24 reqsForTier2, + uint24 reqsForTier3, + uint24 reqsForTier4, + uint24 reqsForTier5 + ) + { + return ( + s_feeConfig.fulfillmentFlatFeeLinkPPMTier1, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier2, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier3, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier4, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier5, + s_feeConfig.reqsForTier2, + s_feeConfig.reqsForTier3, + s_feeConfig.reqsForTier4, + s_feeConfig.reqsForTier5 + ); + } + + function getTotalBalance() external view returns (uint256) { + return s_totalBalance; + } + + function getFallbackWeiPerUnitLink() external view returns (int256) { + return s_fallbackWeiPerUnitLink; + } + + /** + * @notice Owner cancel subscription, sends remaining link directly to the subscription owner. + * @param subId subscription id + * @dev notably can be called even if there are pending requests, outstanding ones may fail onchain + */ + function ownerCancelSubscription(uint64 subId) external onlyOwner { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner); + } + + /** + * @notice Recover link sent with transfer instead of transferAndCall. + * @param to address to send link to + */ + function recoverFunds(address to) external onlyOwner { + uint256 externalBalance = LINK.balanceOf(address(this)); + uint256 internalBalance = uint256(s_totalBalance); + if (internalBalance > externalBalance) { + revert BalanceInvariantViolated(internalBalance, externalBalance); + } + if (internalBalance < externalBalance) { + uint256 amount = externalBalance - internalBalance; + LINK.transfer(to, amount); + emit FundsRecovered(to, amount); + } + // If the balances are equal, nothing to be done. + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function getRequestConfig() + external + view + override + returns ( + uint16, + uint32, + bytes32[] memory + ) + { + return (s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_provingKeyHashes); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function requestRandomWords( + bytes32 keyHash, + uint64 subId, + uint16 requestConfirmations, + uint32 callbackGasLimit, + uint32 numWords + ) external override nonReentrant returns (uint256) { + // Input validation using the subscription storage. + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // Its important to ensure that the consumer is in fact who they say they + // are, otherwise they could use someone else's subscription balance. + // A nonce of 0 indicates consumer is not allocated to the sub. + uint64 currentNonce = s_consumers[msg.sender][subId]; + if (currentNonce == 0) { + revert InvalidConsumer(subId, msg.sender); + } + // Input validation using the config storage word. + if ( + requestConfirmations < s_config.minimumRequestConfirmations || requestConfirmations > MAX_REQUEST_CONFIRMATIONS + ) { + revert InvalidRequestConfirmations( + requestConfirmations, + s_config.minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + // No lower bound on the requested gas limit. A user could request 0 + // and they would simply be billed for the proof verification and wouldn't be + // able to do anything with the random value. + if (callbackGasLimit > s_config.maxGasLimit) { + revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit); + } + if (numWords > MAX_NUM_WORDS) { + revert NumWordsTooBig(numWords, MAX_NUM_WORDS); + } + // Note we do not check whether the keyHash is valid to save gas. + // The consequence for users is that they can send requests + // for invalid keyHashes which will simply not be fulfilled. + uint64 nonce = currentNonce + 1; + (uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, subId, nonce); + + s_requestCommitments[requestId] = keccak256( + abi.encode(requestId, block.number, subId, callbackGasLimit, numWords, msg.sender) + ); + emit RandomWordsRequested( + keyHash, + requestId, + preSeed, + subId, + requestConfirmations, + callbackGasLimit, + numWords, + msg.sender + ); + s_consumers[msg.sender][subId] = nonce; + + return requestId; + } + + /** + * @notice Get request commitment + * @param requestId id of request + * @dev used to determine if a request is fulfilled or not + */ + function getCommitment(uint256 requestId) external view returns (bytes32) { + return s_requestCommitments[requestId]; + } + + function computeRequestId( + bytes32 keyHash, + address sender, + uint64 subId, + uint64 nonce + ) private pure returns (uint256, uint256) { + uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); + return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available. + */ + function callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + // solhint-disable-next-line no-inline-assembly + assembly { + let g := gas() + // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow + // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, GAS_FOR_CALL_EXACT_CHECK) { + revert(0, 0) + } + g := sub(g, GAS_FOR_CALL_EXACT_CHECK) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + function getRandomnessFromProof(Proof memory proof, RequestCommitment memory rc) + private + view + returns ( + bytes32 keyHash, + uint256 requestId, + uint256 randomness + ) + { + keyHash = hashOfKey(proof.pk); + // Only registered proving keys are permitted. + address oracle = s_provingKeys[keyHash]; + if (oracle == address(0)) { + revert NoSuchProvingKey(keyHash); + } + requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); + bytes32 commitment = s_requestCommitments[requestId]; + if (commitment == 0) { + revert NoCorrespondingRequest(); + } + if ( + commitment != keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender)) + ) { + revert IncorrectCommitment(); + } + + bytes32 blockHash = blockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + blockHash = BLOCKHASH_STORE.getBlockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + revert BlockhashNotInStore(rc.blockNum); + } + } + + // The seed actually used by the VRF machinery, mixing in the blockhash + uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); + randomness = VRF.randomValueFromVRFProof(proof, actualSeed); // Reverts on failure + } + + /* + * @notice Compute fee based on the request count + * @param reqCount number of requests + * @return feePPM fee in LINK PPM + */ + function getFeeTier(uint64 reqCount) public view returns (uint32) { + FeeConfig memory fc = s_feeConfig; + if (0 <= reqCount && reqCount <= fc.reqsForTier2) { + return fc.fulfillmentFlatFeeLinkPPMTier1; + } + if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { + return fc.fulfillmentFlatFeeLinkPPMTier2; + } + if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { + return fc.fulfillmentFlatFeeLinkPPMTier3; + } + if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { + return fc.fulfillmentFlatFeeLinkPPMTier4; + } + return fc.fulfillmentFlatFeeLinkPPMTier5; + } + + /* + * @notice Fulfill a randomness request + * @param proof contains the proof and randomness + * @param rc request commitment pre-image, committed to at request time + * @return payment amount billed to the subscription + * @dev simulated offchain to determine if sufficient balance is present to fulfill the request + */ + function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint96) { + uint256 startGas = gasleft(); + (bytes32 keyHash, uint256 requestId, uint256 randomness) = getRandomnessFromProof(proof, rc); + + uint256[] memory randomWords = new uint256[](rc.numWords); + for (uint256 i = 0; i < rc.numWords; i++) { + randomWords[i] = uint256(keccak256(abi.encode(randomness, i))); + } + + delete s_requestCommitments[requestId]; + VRFConsumerBaseV2 v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, requestId, randomWords); + // Call with explicitly the amount of callback gas requested + // Important to not let them exhaust the gas budget and avoid oracle payment. + // Do not allow any non-view/non-pure coordinator functions to be called + // during the consumers callback code via reentrancyLock. + // Note that callWithExactGas will revert if we do not have sufficient gas + // to give the callee their requested amount. + s_config.reentrancyLock = true; + bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp); + s_config.reentrancyLock = false; + + // Increment the req count for fee tier selection. + uint64 reqCount = s_subscriptions[rc.subId].reqCount; + s_subscriptions[rc.subId].reqCount += 1; + + // We want to charge users exactly for how much gas they use in their callback. + // The gasAfterPaymentCalculation is meant to cover these additional operations where we + // decrement the subscription balance and increment the oracles withdrawable balance. + // We also add the flat link fee to the payment amount. + // Its specified in millionths of link, if s_config.fulfillmentFlatFeeLinkPPM = 1 + // 1 link / 1e6 = 1e18 juels / 1e6 = 1e12 juels. + uint96 payment = calculatePaymentAmount( + startGas, + s_config.gasAfterPaymentCalculation, + getFeeTier(reqCount), + tx.gasprice + ); + if (s_subscriptions[rc.subId].balance < payment) { + revert InsufficientBalance(); + } + s_subscriptions[rc.subId].balance -= payment; + s_withdrawableTokens[s_provingKeys[keyHash]] += payment; + // Include payment in the event for tracking costs. + emit RandomWordsFulfilled(requestId, randomness, payment, success); + return payment; + } + + // Get the amount of gas used for fulfillment + function calculatePaymentAmount( + uint256 startGas, + uint256 gasAfterPaymentCalculation, + uint32 fulfillmentFlatFeeLinkPPM, + uint256 weiPerUnitGas + ) internal view returns (uint96) { + int256 weiPerUnitLink; + weiPerUnitLink = getFeedData(); + if (weiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(weiPerUnitLink); + } + // (1e18 juels/link) (wei/gas * gas) / (wei/link) = juels + uint256 paymentNoFee = (1e18 * weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft())) / + uint256(weiPerUnitLink); + uint256 fee = 1e12 * uint256(fulfillmentFlatFeeLinkPPM); + if (paymentNoFee > (1e27 - fee)) { + revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. + } + return uint96(paymentNoFee + fee); + } + + function getFeedData() private view returns (int256) { + uint32 stalenessSeconds = s_config.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 weiPerUnitLink; + (, weiPerUnitLink, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + // solhint-disable-next-line not-rely-on-time + if (staleFallback && stalenessSeconds < block.timestamp - timestamp) { + weiPerUnitLink = s_fallbackWeiPerUnitLink; + } + return weiPerUnitLink; + } + + /* + * @notice Oracle withdraw LINK earned through fulfilling requests + * @param recipient where to send the funds + * @param amount amount to withdraw + */ + function oracleWithdraw(address recipient, uint96 amount) external nonReentrant { + if (s_withdrawableTokens[msg.sender] < amount) { + revert InsufficientBalance(); + } + s_withdrawableTokens[msg.sender] -= amount; + s_totalBalance -= amount; + if (!LINK.transfer(recipient, amount)) { + revert InsufficientBalance(); + } + } + + function onTokenTransfer( + address, /* sender */ + uint256 amount, + bytes calldata data + ) external override nonReentrant { + if (msg.sender != address(LINK)) { + revert OnlyCallableFromLink(); + } + if (data.length != 32) { + revert InvalidCalldata(); + } + uint64 subId = abi.decode(data, (uint64)); + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // We do not check that the msg.sender is the subscription owner, + // anyone can fund a subscription. + uint256 oldBalance = s_subscriptions[subId].balance; + s_subscriptions[subId].balance += uint96(amount); + s_totalBalance += uint96(amount); + emit SubscriptionFunded(subId, oldBalance, oldBalance + amount); + } + + function getCurrentSubId() external view returns (uint64) { + return s_currentSubId; + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function getSubscription(uint64 subId) + external + view + override + returns ( + uint96 balance, + uint64 reqCount, + address owner, + address[] memory consumers + ) + { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + return ( + s_subscriptions[subId].balance, + s_subscriptions[subId].reqCount, + s_subscriptionConfigs[subId].owner, + s_subscriptionConfigs[subId].consumers + ); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function createSubscription() external override nonReentrant returns (uint64) { + s_currentSubId++; + uint64 currentSubId = s_currentSubId; + address[] memory consumers = new address[](0); + s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0}); + s_subscriptionConfigs[currentSubId] = SubscriptionConfig({ + owner: msg.sender, + requestedOwner: address(0), + consumers: consumers + }); + + emit SubscriptionCreated(currentSubId, msg.sender); + return currentSubId; + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) + external + override + onlySubOwner(subId) + nonReentrant + { + // Proposing to address(0) would never be claimable so don't need to check. + if (s_subscriptionConfigs[subId].requestedOwner != newOwner) { + s_subscriptionConfigs[subId].requestedOwner = newOwner; + emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); + } + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function acceptSubscriptionOwnerTransfer(uint64 subId) external override nonReentrant { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) { + revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner); + } + address oldOwner = s_subscriptionConfigs[subId].owner; + s_subscriptionConfigs[subId].owner = msg.sender; + s_subscriptionConfigs[subId].requestedOwner = address(0); + emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function removeConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + if (s_consumers[consumer][subId] == 0) { + revert InvalidConsumer(subId, consumer); + } + // Note bounded by MAX_CONSUMERS + address[] memory consumers = s_subscriptionConfigs[subId].consumers; + uint256 lastConsumerIndex = consumers.length - 1; + for (uint256 i = 0; i < consumers.length; i++) { + if (consumers[i] == consumer) { + address last = consumers[lastConsumerIndex]; + // Storage write to preserve last element + s_subscriptionConfigs[subId].consumers[i] = last; + // Storage remove last element + s_subscriptionConfigs[subId].consumers.pop(); + break; + } + } + delete s_consumers[consumer][subId]; + emit SubscriptionConsumerRemoved(subId, consumer); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + // Already maxed, cannot add any more consumers. + if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) { + revert TooManyConsumers(); + } + if (s_consumers[consumer][subId] != 0) { + // Idempotence - do nothing if already added. + // Ensures uniqueness in s_subscriptions[subId].consumers. + return; + } + // Initialize the nonce to 1, indicating the consumer is allocated. + s_consumers[consumer][subId] = 1; + s_subscriptionConfigs[subId].consumers.push(consumer); + + emit SubscriptionConsumerAdded(subId, consumer); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function cancelSubscription(uint64 subId, address to) external override onlySubOwner(subId) nonReentrant { + if (pendingRequestExists(subId)) { + revert PendingRequestExists(); + } + cancelSubscriptionHelper(subId, to); + } + + function cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + Subscription memory sub = s_subscriptions[subId]; + uint96 balance = sub.balance; + // Note bounded by MAX_CONSUMERS; + // If no consumers, does nothing. + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + delete s_consumers[subConfig.consumers[i]][subId]; + } + delete s_subscriptionConfigs[subId]; + delete s_subscriptions[subId]; + s_totalBalance -= balance; + if (!LINK.transfer(to, uint256(balance))) { + revert InsufficientBalance(); + } + emit SubscriptionCanceled(subId, to, balance); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + * @dev Looping is bounded to MAX_CONSUMERS*(number of keyhashes). + * @dev Used to disable subscription canceling while outstanding request are present. + */ + function pendingRequestExists(uint64 subId) public view override returns (bool) { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + for (uint256 j = 0; j < s_provingKeyHashes.length; j++) { + (uint256 reqId, ) = computeRequestId( + s_provingKeyHashes[j], + subConfig.consumers[i], + subId, + s_consumers[subConfig.consumers[i]][subId] + ); + if (s_requestCommitments[reqId] != 0) { + return true; + } + } + } + return false; + } + + modifier onlySubOwner(uint64 subId) { + address owner = s_subscriptionConfigs[subId].owner; + if (owner == address(0)) { + revert InvalidSubscription(); + } + if (msg.sender != owner) { + revert MustBeSubOwner(owner); + } + _; + } + + modifier nonReentrant() { + if (s_config.reentrancyLock) { + revert Reentrant(); + } + _; + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "VRFCoordinatorV2 1.0.0"; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFRequestIDBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFRequestIDBase.sol new file mode 100644 index 0000000..7770640 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFRequestIDBase.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract VRFRequestIDBase { + /** + * @notice returns the seed which is actually input to the VRF coordinator + * + * @dev To prevent repetition of VRF output due to repetition of the + * @dev user-supplied seed, that seed is combined in a hash with the + * @dev user-specific nonce, and the address of the consuming contract. The + * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in + * @dev the final seed, but the nonce does protect against repetition in + * @dev requests which are included in a single block. + * + * @param _userSeed VRF seed input provided by user + * @param _requester Address of the requesting contract + * @param _nonce User-specific nonce at the time of the request + */ + function makeVRFInputSeed( + bytes32 _keyHash, + uint256 _userSeed, + address _requester, + uint256 _nonce + ) internal pure returns (uint256) { + return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); + } + + /** + * @notice Returns the id for this request + * @param _keyHash The serviceAgreement ID to be used for this request + * @param _vRFInputSeed The seed to be passed directly to the VRF + * @return The id for this request + * + * @dev Note that _vRFInputSeed is not the seed passed by the consuming + * @dev contract, but the one generated by makeVRFInputSeed + */ + function makeRequestId(bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFTypes.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFTypes.sol new file mode 100644 index 0000000..95a32fb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFTypes.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +/** + * @title VRFTypes + * @notice The VRFTypes library is a collection of types that is required to fulfill VRF requests + * on-chain. They must be ABI-compatible with the types used by the coordinator contracts. + */ +library VRFTypes { + // ABI-compatible with VRF.Proof. + // This proof is used for VRF V2. + struct Proof { + uint256[2] pk; + uint256[2] gamma; + uint256 c; + uint256 s; + uint256 seed; + address uWitness; + uint256[2] cGammaWitness; + uint256[2] sHashWitness; + uint256 zInv; + } + + // ABI-compatible with VRFCoordinatorV2.RequestCommitment. + // This is only used for VRF V2. + struct RequestCommitment { + uint64 blockNum; + uint64 subId; + uint32 callbackGasLimit; + uint32 numWords; + address sender; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFV2Wrapper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFV2Wrapper.sol new file mode 100644 index 0000000..25daee0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFV2Wrapper.sol @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "./ConfirmedOwner.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; +import "./VRFConsumerBaseV2.sol"; +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/AggregatorV3Interface.sol"; +import "./interfaces/VRFCoordinatorV2Interface.sol"; +import "./interfaces/VRFV2WrapperInterface.sol"; +import "./VRFV2WrapperConsumerBase.sol"; + +/** + * @notice A wrapper for VRFCoordinatorV2 that provides an interface better suited to one-off + * @notice requests for randomness. + */ +contract VRFV2Wrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsumerBaseV2, VRFV2WrapperInterface { + event WrapperFulfillmentFailed(uint256 indexed requestId, address indexed consumer); + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + ExtendedVRFCoordinatorV2Interface public immutable COORDINATOR; + uint64 public immutable SUBSCRIPTION_ID; + + // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) + // and some arithmetic operations. + uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; + + // lastRequestId is the request ID of the most recent VRF V2 request made by this wrapper. This + // should only be relied on within the same transaction the request was made. + uint256 public override lastRequestId; + + // Configuration fetched from VRFCoordinatorV2 + + // s_configured tracks whether this contract has been configured. If not configured, randomness + // requests cannot be made. + bool public s_configured; + + // s_disabled disables the contract when true. When disabled, new VRF requests cannot be made + // but existing ones can still be fulfilled. + bool public s_disabled; + + // s_fallbackWeiPerUnitLink is the backup LINK exchange rate used when the LINK/NATIVE feed is + // stale. + int256 private s_fallbackWeiPerUnitLink; + + // s_stalenessSeconds is the number of seconds before we consider the feed price to be stale and + // fallback to fallbackWeiPerUnitLink. + uint32 private s_stalenessSeconds; + + // s_fulfillmentFlatFeeLinkPPM is the flat fee in millionths of LINK that VRFCoordinatorV2 + // charges. + uint32 private s_fulfillmentFlatFeeLinkPPM; + + // Other configuration + + // s_wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords + // function. The cost for this gas is passed to the user. + uint32 private s_wrapperGasOverhead; + + // s_coordinatorGasOverhead reflects the gas overhead of the coordinator's fulfillRandomWords + // function. The cost for this gas is billed to the subscription, and must therefor be included + // in the pricing for wrapped requests. This includes the gas costs of proof verification and + // payment calculation in the coordinator. + uint32 private s_coordinatorGasOverhead; + + // s_wrapperPremiumPercentage is the premium ratio in percentage. For example, a value of 0 + // indicates no premium. A value of 15 indicates a 15 percent premium. + uint8 private s_wrapperPremiumPercentage; + + // s_keyHash is the key hash to use when requesting randomness. Fees are paid based on current gas + // fees, so this should be set to the highest gas lane on the network. + bytes32 s_keyHash; + + // s_maxNumWords is the max number of words that can be requested in a single wrapped VRF request. + uint8 s_maxNumWords; + + struct Callback { + address callbackAddress; + uint32 callbackGasLimit; + uint256 requestGasPrice; + int256 requestWeiPerUnitLink; + uint256 juelsPaid; + } + mapping(uint256 => Callback) /* requestID */ /* callback */ + public s_callbacks; + + constructor( + address _link, + address _linkEthFeed, + address _coordinator + ) ConfirmedOwner(msg.sender) VRFConsumerBaseV2(_coordinator) { + LINK = LinkTokenInterface(_link); + LINK_ETH_FEED = AggregatorV3Interface(_linkEthFeed); + COORDINATOR = ExtendedVRFCoordinatorV2Interface(_coordinator); + + // Create this wrapper's subscription and add itself as a consumer. + uint64 subId = ExtendedVRFCoordinatorV2Interface(_coordinator).createSubscription(); + SUBSCRIPTION_ID = subId; + ExtendedVRFCoordinatorV2Interface(_coordinator).addConsumer(subId, address(this)); + } + + /** + * @notice setConfig configures VRFV2Wrapper. + * + * @dev Sets wrapper-specific configuration based on the given parameters, and fetches any needed + * @dev VRFCoordinatorV2 configuration from the coordinator. + * + * @param _wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords + * function. + * + * @param _coordinatorGasOverhead reflects the gas overhead of the coordinator's + * fulfillRandomWords function. + * + * @param _wrapperPremiumPercentage is the premium ratio in percentage for wrapper requests. + * + * @param _keyHash to use for requesting randomness. + */ + function setConfig( + uint32 _wrapperGasOverhead, + uint32 _coordinatorGasOverhead, + uint8 _wrapperPremiumPercentage, + bytes32 _keyHash, + uint8 _maxNumWords + ) external onlyOwner { + s_wrapperGasOverhead = _wrapperGasOverhead; + s_coordinatorGasOverhead = _coordinatorGasOverhead; + s_wrapperPremiumPercentage = _wrapperPremiumPercentage; + s_keyHash = _keyHash; + s_maxNumWords = _maxNumWords; + s_configured = true; + + // Get other configuration from coordinator + (, , s_stalenessSeconds, ) = COORDINATOR.getConfig(); + s_fallbackWeiPerUnitLink = COORDINATOR.getFallbackWeiPerUnitLink(); + (s_fulfillmentFlatFeeLinkPPM, , , , , , , , ) = COORDINATOR.getFeeConfig(); + } + + /** + * @notice getConfig returns the current VRFV2Wrapper configuration. + * + * @return fallbackWeiPerUnitLink is the backup LINK exchange rate used when the LINK/NATIVE feed + * is stale. + * + * @return stalenessSeconds is the number of seconds before we consider the feed price to be stale + * and fallback to fallbackWeiPerUnitLink. + * + * @return fulfillmentFlatFeeLinkPPM is the flat fee in millionths of LINK that VRFCoordinatorV2 + * charges. + * + * @return wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords + * function. The cost for this gas is passed to the user. + * + * @return coordinatorGasOverhead reflects the gas overhead of the coordinator's + * fulfillRandomWords function. + * + * @return wrapperPremiumPercentage is the premium ratio in percentage. For example, a value of 0 + * indicates no premium. A value of 15 indicates a 15 percent premium. + * + * @return keyHash is the key hash to use when requesting randomness. Fees are paid based on + * current gas fees, so this should be set to the highest gas lane on the network. + * + * @return maxNumWords is the max number of words that can be requested in a single wrapped VRF + * request. + */ + function getConfig() + external + view + returns ( + int256 fallbackWeiPerUnitLink, + uint32 stalenessSeconds, + uint32 fulfillmentFlatFeeLinkPPM, + uint32 wrapperGasOverhead, + uint32 coordinatorGasOverhead, + uint8 wrapperPremiumPercentage, + bytes32 keyHash, + uint8 maxNumWords + ) + { + return ( + s_fallbackWeiPerUnitLink, + s_stalenessSeconds, + s_fulfillmentFlatFeeLinkPPM, + s_wrapperGasOverhead, + s_coordinatorGasOverhead, + s_wrapperPremiumPercentage, + s_keyHash, + s_maxNumWords + ); + } + + /** + * @notice Calculates the price of a VRF request with the given callbackGasLimit at the current + * @notice block. + * + * @dev This function relies on the transaction gas price which is not automatically set during + * @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function. + * + * @param _callbackGasLimit is the gas limit used to estimate the price. + */ + function calculateRequestPrice(uint32 _callbackGasLimit) + external + view + override + onlyConfiguredNotDisabled + returns (uint256) + { + int256 weiPerUnitLink = getFeedData(); + return calculateRequestPriceInternal(_callbackGasLimit, tx.gasprice, weiPerUnitLink); + } + + /** + * @notice Estimates the price of a VRF request with a specific gas limit and gas price. + * + * @dev This is a convenience function that can be called in simulation to better understand + * @dev pricing. + * + * @param _callbackGasLimit is the gas limit used to estimate the price. + * @param _requestGasPriceWei is the gas price in wei used for the estimation. + */ + function estimateRequestPrice(uint32 _callbackGasLimit, uint256 _requestGasPriceWei) + external + view + override + onlyConfiguredNotDisabled + returns (uint256) + { + int256 weiPerUnitLink = getFeedData(); + return calculateRequestPriceInternal(_callbackGasLimit, _requestGasPriceWei, weiPerUnitLink); + } + + function calculateRequestPriceInternal( + uint256 _gas, + uint256 _requestGasPrice, + int256 _weiPerUnitLink + ) internal view returns (uint256) { + uint256 baseFee = (1e18 * _requestGasPrice * (_gas + s_wrapperGasOverhead + s_coordinatorGasOverhead)) / + uint256(_weiPerUnitLink); + + uint256 feeWithPremium = (baseFee * (s_wrapperPremiumPercentage + 100)) / 100; + + uint256 feeWithFlatFee = feeWithPremium + (1e12 * uint256(s_fulfillmentFlatFeeLinkPPM)); + + return feeWithFlatFee; + } + + /** + * @notice onTokenTransfer is called by LinkToken upon payment for a VRF request. + * + * @dev Reverts if payment is too low. + * + * @param _sender is the sender of the payment, and the address that will receive a VRF callback + * upon fulfillment. + * + * @param _amount is the amount of LINK paid in Juels. + * + * @param _data is the abi-encoded VRF request parameters: uint32 callbackGasLimit, + * uint16 requestConfirmations, and uint32 numWords. + */ + function onTokenTransfer( + address _sender, + uint256 _amount, + bytes calldata _data + ) external onlyConfiguredNotDisabled { + require(msg.sender == address(LINK), "only callable from LINK"); + + (uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords) = abi.decode( + _data, + (uint32, uint16, uint32) + ); + uint32 eip150Overhead = getEIP150Overhead(callbackGasLimit); + int256 weiPerUnitLink = getFeedData(); + uint256 price = calculateRequestPriceInternal(callbackGasLimit, tx.gasprice, weiPerUnitLink); + require(_amount >= price, "fee too low"); + require(numWords <= s_maxNumWords, "numWords too high"); + + uint256 requestId = COORDINATOR.requestRandomWords( + s_keyHash, + SUBSCRIPTION_ID, + requestConfirmations, + callbackGasLimit + eip150Overhead + s_wrapperGasOverhead, + numWords + ); + s_callbacks[requestId] = Callback({ + callbackAddress: _sender, + callbackGasLimit: callbackGasLimit, + requestGasPrice: tx.gasprice, + requestWeiPerUnitLink: weiPerUnitLink, + juelsPaid: _amount + }); + lastRequestId = requestId; + } + + /** + * @notice withdraw is used by the VRFV2Wrapper's owner to withdraw LINK revenue. + * + * @param _recipient is the address that should receive the LINK funds. + * + * @param _amount is the amount of LINK in Juels that should be withdrawn. + */ + function withdraw(address _recipient, uint256 _amount) external onlyOwner { + LINK.transfer(_recipient, _amount); + } + + /** + * @notice enable this contract so that new requests can be accepted. + */ + function enable() external onlyOwner { + s_disabled = false; + } + + /** + * @notice disable this contract so that new requests will be rejected. When disabled, new requests + * @notice will revert but existing requests can still be fulfilled. + */ + function disable() external onlyOwner { + s_disabled = true; + } + + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override { + Callback memory callback = s_callbacks[_requestId]; + delete s_callbacks[_requestId]; + require(callback.callbackAddress != address(0), "request not found"); // This should never happen + + VRFV2WrapperConsumerBase c; + bytes memory resp = abi.encodeWithSelector(c.rawFulfillRandomWords.selector, _requestId, _randomWords); + + bool success = callWithExactGas(callback.callbackGasLimit, callback.callbackAddress, resp); + if (!success) { + emit WrapperFulfillmentFailed(_requestId, callback.callbackAddress); + } + } + + function getFeedData() private view returns (int256) { + bool staleFallback = s_stalenessSeconds > 0; + uint256 timestamp; + int256 weiPerUnitLink; + (, weiPerUnitLink, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + // solhint-disable-next-line not-rely-on-time + if (staleFallback && s_stalenessSeconds < block.timestamp - timestamp) { + weiPerUnitLink = s_fallbackWeiPerUnitLink; + } + require(weiPerUnitLink >= 0, "Invalid LINK wei price"); + return weiPerUnitLink; + } + + /** + * @dev Calculates extra amount of gas required for running an assembly call() post-EIP150. + */ + function getEIP150Overhead(uint32 gas) private pure returns (uint32) { + return gas / 63 + 1; + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available. + */ + function callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + // solhint-disable-next-line no-inline-assembly + assembly { + let g := gas() + // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow + // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, GAS_FOR_CALL_EXACT_CHECK) { + revert(0, 0) + } + g := sub(g, GAS_FOR_CALL_EXACT_CHECK) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + function typeAndVersion() external pure virtual override returns (string memory) { + return "VRFV2Wrapper 1.0.0"; + } + + modifier onlyConfiguredNotDisabled() { + require(s_configured, "wrapper is not configured"); + require(!s_disabled, "wrapper is disabled"); + _; + } +} + +interface ExtendedVRFCoordinatorV2Interface is VRFCoordinatorV2Interface { + function getConfig() + external + view + returns ( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation + ); + + function getFallbackWeiPerUnitLink() external view returns (int256); + + function getFeeConfig() + external + view + returns ( + uint32 fulfillmentFlatFeeLinkPPMTier1, + uint32 fulfillmentFlatFeeLinkPPMTier2, + uint32 fulfillmentFlatFeeLinkPPMTier3, + uint32 fulfillmentFlatFeeLinkPPMTier4, + uint32 fulfillmentFlatFeeLinkPPMTier5, + uint24 reqsForTier2, + uint24 reqsForTier3, + uint24 reqsForTier4, + uint24 reqsForTier5 + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol new file mode 100644 index 0000000..301e757 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./interfaces/LinkTokenInterface.sol"; +import "./interfaces/VRFV2WrapperInterface.sol"; + +/** ******************************************************************************* + * @notice Interface for contracts using VRF randomness through the VRF V2 wrapper + * ******************************************************************************** + * @dev PURPOSE + * + * @dev Create VRF V2 requests without the need for subscription management. Rather than creating + * @dev and funding a VRF V2 subscription, a user can use this wrapper to create one off requests, + * @dev paying up front rather than at fulfillment. + * + * @dev Since the price is determined using the gas price of the request transaction rather than + * @dev the fulfillment transaction, the wrapper charges an additional premium on callback gas + * @dev usage, in addition to some extra overhead costs associated with the VRFV2Wrapper contract. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFV2WrapperConsumerBase. The consumer must be funded + * @dev with enough LINK to make the request, otherwise requests will revert. To request randomness, + * @dev call the 'requestRandomness' function with the desired VRF parameters. This function handles + * @dev paying for the request based on the current pricing. + * + * @dev Consumers must implement the fullfillRandomWords function, which will be called during + * @dev fulfillment with the randomness result. + */ +abstract contract VRFV2WrapperConsumerBase { + LinkTokenInterface internal immutable LINK; + VRFV2WrapperInterface internal immutable VRF_V2_WRAPPER; + + /** + * @param _link is the address of LinkToken + * @param _vrfV2Wrapper is the address of the VRFV2Wrapper contract + */ + constructor(address _link, address _vrfV2Wrapper) { + LINK = LinkTokenInterface(_link); + VRF_V2_WRAPPER = VRFV2WrapperInterface(_vrfV2Wrapper); + } + + /** + * @dev Requests randomness from the VRF V2 wrapper. + * + * @param _callbackGasLimit is the gas limit that should be used when calling the consumer's + * fulfillRandomWords function. + * @param _requestConfirmations is the number of confirmations to wait before fulfilling the + * request. A higher number of confirmations increases security by reducing the likelihood + * that a chain re-org changes a published randomness outcome. + * @param _numWords is the number of random words to request. + * + * @return requestId is the VRF V2 request ID of the newly created randomness request. + */ + function requestRandomness( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords + ) internal returns (uint256 requestId) { + LINK.transferAndCall( + address(VRF_V2_WRAPPER), + VRF_V2_WRAPPER.calculateRequestPrice(_callbackGasLimit), + abi.encode(_callbackGasLimit, _requestConfirmations, _numWords) + ); + return VRF_V2_WRAPPER.lastRequestId(); + } + + /** + * @notice fulfillRandomWords handles the VRF V2 wrapper response. The consuming contract must + * @notice implement it. + * + * @param _requestId is the VRF V2 request ID. + * @param _randomWords is the randomness result. + */ + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal virtual; + + function rawFulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) external { + require(msg.sender == address(VRF_V2_WRAPPER), "only VRF V2 wrapper can fulfill"); + fulfillRandomWords(_requestId, _randomWords); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/ValidatorProxy.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ValidatorProxy.sol new file mode 100644 index 0000000..581b255 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/ValidatorProxy.sol @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./ConfirmedOwner.sol"; +import "./interfaces/AggregatorValidatorInterface.sol"; +import "./interfaces/TypeAndVersionInterface.sol"; + +contract ValidatorProxy is AggregatorValidatorInterface, TypeAndVersionInterface, ConfirmedOwner { + /// @notice Uses a single storage slot to store the current address + struct AggregatorConfiguration { + address target; + bool hasNewProposal; + } + + struct ValidatorConfiguration { + AggregatorValidatorInterface target; + bool hasNewProposal; + } + + // Configuration for the current aggregator + AggregatorConfiguration private s_currentAggregator; + // Proposed aggregator address + address private s_proposedAggregator; + + // Configuration for the current validator + ValidatorConfiguration private s_currentValidator; + // Proposed validator address + AggregatorValidatorInterface private s_proposedValidator; + + event AggregatorProposed(address indexed aggregator); + event AggregatorUpgraded(address indexed previous, address indexed current); + event ValidatorProposed(AggregatorValidatorInterface indexed validator); + event ValidatorUpgraded(AggregatorValidatorInterface indexed previous, AggregatorValidatorInterface indexed current); + /// @notice The proposed aggregator called validate, but the call was not passed on to any validators + event ProposedAggregatorValidateCall( + address indexed proposed, + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ); + + /** + * @notice Construct the ValidatorProxy with an aggregator and a validator + * @param aggregator address + * @param validator address + */ + constructor(address aggregator, AggregatorValidatorInterface validator) ConfirmedOwner(msg.sender) { + s_currentAggregator = AggregatorConfiguration({target: aggregator, hasNewProposal: false}); + s_currentValidator = ValidatorConfiguration({target: validator, hasNewProposal: false}); + } + + /** + * @notice Validate a transmission + * @dev Must be called by either the `s_currentAggregator.target`, or the `s_proposedAggregator`. + * If called by the `s_currentAggregator.target` this function passes the call on to the `s_currentValidator.target` + * and the `s_proposedValidator`, if it is set. + * If called by the `s_proposedAggregator` this function emits a `ProposedAggregatorValidateCall` to signal that + * the call was received. + * @dev To guard against external `validate` calls reverting, we use raw calls here. + * We favour `call` over try-catch to ensure that failures are avoided even if the validator address is incorrectly + * set as a non-contract address. + * @dev If the `aggregator` and `validator` are the same contract or collude, this could exhibit reentrancy behavior. + * However, since that contract would have to be explicitly written for reentrancy and that the `owner` would have + * to configure this contract to use that malicious contract, we refrain from using mutex or check here. + * @dev This does not perform any checks on any roundId, so it is possible that a validator receive different reports + * for the same roundId at different points in time. Validator implementations should be aware of this. + * @param previousRoundId uint256 + * @param previousAnswer int256 + * @param currentRoundId uint256 + * @param currentAnswer int256 + * @return bool + */ + function validate( + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ) external override returns (bool) { + address currentAggregator = s_currentAggregator.target; + if (msg.sender != currentAggregator) { + address proposedAggregator = s_proposedAggregator; + require(msg.sender == proposedAggregator, "Not a configured aggregator"); + // If the aggregator is still in proposed state, emit an event and don't push to any validator. + // This is to confirm that `validate` is being called prior to upgrade. + emit ProposedAggregatorValidateCall( + proposedAggregator, + previousRoundId, + previousAnswer, + currentRoundId, + currentAnswer + ); + return true; + } + + // Send the validate call to the current validator + ValidatorConfiguration memory currentValidator = s_currentValidator; + address currentValidatorAddress = address(currentValidator.target); + require(currentValidatorAddress != address(0), "No validator set"); + currentValidatorAddress.call( + abi.encodeWithSelector( + AggregatorValidatorInterface.validate.selector, + previousRoundId, + previousAnswer, + currentRoundId, + currentAnswer + ) + ); + // If there is a new proposed validator, send the validate call to that validator also + if (currentValidator.hasNewProposal) { + address(s_proposedValidator).call( + abi.encodeWithSelector( + AggregatorValidatorInterface.validate.selector, + previousRoundId, + previousAnswer, + currentRoundId, + currentAnswer + ) + ); + } + return true; + } + + /** AGGREGATOR CONFIGURATION FUNCTIONS **/ + + /** + * @notice Propose an aggregator + * @dev A zero address can be used to unset the proposed aggregator. Only owner can call. + * @param proposed address + */ + function proposeNewAggregator(address proposed) external onlyOwner { + require(s_proposedAggregator != proposed && s_currentAggregator.target != proposed, "Invalid proposal"); + s_proposedAggregator = proposed; + // If proposed is zero address, hasNewProposal = false + s_currentAggregator.hasNewProposal = (proposed != address(0)); + emit AggregatorProposed(proposed); + } + + /** + * @notice Upgrade the aggregator by setting the current aggregator as the proposed aggregator. + * @dev Must have a proposed aggregator. Only owner can call. + */ + function upgradeAggregator() external onlyOwner { + // Get configuration in memory + AggregatorConfiguration memory current = s_currentAggregator; + address previous = current.target; + address proposed = s_proposedAggregator; + + // Perform the upgrade + require(current.hasNewProposal, "No proposal"); + s_currentAggregator = AggregatorConfiguration({target: proposed, hasNewProposal: false}); + delete s_proposedAggregator; + + emit AggregatorUpgraded(previous, proposed); + } + + /** + * @notice Get aggregator details + * @return current address + * @return hasProposal bool + * @return proposed address + */ + function getAggregators() + external + view + returns ( + address current, + bool hasProposal, + address proposed + ) + { + current = s_currentAggregator.target; + hasProposal = s_currentAggregator.hasNewProposal; + proposed = s_proposedAggregator; + } + + /** VALIDATOR CONFIGURATION FUNCTIONS **/ + + /** + * @notice Propose an validator + * @dev A zero address can be used to unset the proposed validator. Only owner can call. + * @param proposed address + */ + function proposeNewValidator(AggregatorValidatorInterface proposed) external onlyOwner { + require(s_proposedValidator != proposed && s_currentValidator.target != proposed, "Invalid proposal"); + s_proposedValidator = proposed; + // If proposed is zero address, hasNewProposal = false + s_currentValidator.hasNewProposal = (address(proposed) != address(0)); + emit ValidatorProposed(proposed); + } + + /** + * @notice Upgrade the validator by setting the current validator as the proposed validator. + * @dev Must have a proposed validator. Only owner can call. + */ + function upgradeValidator() external onlyOwner { + // Get configuration in memory + ValidatorConfiguration memory current = s_currentValidator; + AggregatorValidatorInterface previous = current.target; + AggregatorValidatorInterface proposed = s_proposedValidator; + + // Perform the upgrade + require(current.hasNewProposal, "No proposal"); + s_currentValidator = ValidatorConfiguration({target: proposed, hasNewProposal: false}); + delete s_proposedValidator; + + emit ValidatorUpgraded(previous, proposed); + } + + /** + * @notice Get validator details + * @return current address + * @return hasProposal bool + * @return proposed address + */ + function getValidators() + external + view + returns ( + AggregatorValidatorInterface current, + bool hasProposal, + AggregatorValidatorInterface proposed + ) + { + current = s_currentValidator.target; + hasProposal = s_currentValidator.hasNewProposal; + proposed = s_proposedValidator; + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "ValidatorProxy 1.0.0"; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumCrossDomainForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumCrossDomainForwarder.sol new file mode 100644 index 0000000..63cae52 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumCrossDomainForwarder.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/TypeAndVersionInterface.sol"; +import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; +import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; +import "./CrossDomainForwarder.sol"; + +/** + * @title ArbitrumCrossDomainForwarder - L1 xDomain account representation + * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. + * @dev Any other L2 contract which uses this contract's address as a privileged position, + * can be considered to be owned by the `l1Owner` + */ +contract ArbitrumCrossDomainForwarder is TypeAndVersionInterface, CrossDomainForwarder { + /** + * @notice creates a new Arbitrum xDomain Forwarder contract + * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn + * @dev Empty constructor required due to inheriting from abstract contract CrossDomainForwarder + */ + constructor(address l1OwnerAddr) CrossDomainOwnable(l1OwnerAddr) {} + + /** + * @notice versions: + * + * - ArbitrumCrossDomainForwarder 0.1.0: initial release + * - ArbitrumCrossDomainForwarder 1.0.0: Use OZ Address, CrossDomainOwnable + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "ArbitrumCrossDomainForwarder 1.0.0"; + } + + /** + * @notice The L2 xDomain `msg.sender`, generated from L1 sender address + */ + function crossDomainMessenger() public view returns (address) { + return AddressAliasHelper.applyL1ToL2Alias(l1Owner()); + } + + /** + * @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` being the L1 owner address + * @inheritdoc ForwarderInterface + */ + function forward(address target, bytes memory data) external virtual override onlyL1Owner { + Address.functionCall(target, data, "Forwarder call reverted"); + } + + /** + * @notice The call MUST come from the L1 owner (via cross-chain message.) Reverts otherwise. + */ + modifier onlyL1Owner() override { + require(msg.sender == crossDomainMessenger(), "Sender is not the L2 messenger"); + _; + } + + /** + * @notice The call MUST come from the proposed L1 owner (via cross-chain message.) Reverts otherwise. + */ + modifier onlyProposedL1Owner() override { + require(msg.sender == AddressAliasHelper.applyL1ToL2Alias(s_l1PendingOwner), "Must be proposed L1 owner"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumCrossDomainGovernor.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumCrossDomainGovernor.sol new file mode 100644 index 0000000..930926c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumCrossDomainGovernor.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./interfaces/DelegateForwarderInterface.sol"; +import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; +import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; +import "./ArbitrumCrossDomainForwarder.sol"; + +/** + * @title ArbitrumCrossDomainGovernor - L1 xDomain account representation (with delegatecall support) for Arbitrum + * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. + * @dev Any other L2 contract which uses this contract's address as a privileged position, + * can be considered to be simultaneously owned by the `l1Owner` and L2 `owner` + */ +contract ArbitrumCrossDomainGovernor is DelegateForwarderInterface, ArbitrumCrossDomainForwarder { + /** + * @notice creates a new Arbitrum xDomain Forwarder contract + * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn + * @dev Empty constructor required due to inheriting from abstract contract CrossDomainForwarder + */ + constructor(address l1OwnerAddr) ArbitrumCrossDomainForwarder(l1OwnerAddr) {} + + /** + * @notice versions: + * + * - ArbitrumCrossDomainGovernor 1.0.0: initial release + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "ArbitrumCrossDomainGovernor 1.0.0"; + } + + /** + * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner + * @inheritdoc ForwarderInterface + */ + function forward(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { + Address.functionCall(target, data, "Governor call reverted"); + } + + /** + * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner + * @inheritdoc DelegateForwarderInterface + */ + function forwardDelegate(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { + Address.functionDelegateCall(target, data, "Governor delegatecall reverted"); + } + + /** + * @notice The call MUST come from either the L1 owner (via cross-chain message) or the L2 owner. Reverts otherwise. + */ + modifier onlyLocalOrCrossDomainOwner() { + require(msg.sender == crossDomainMessenger() || msg.sender == owner(), "Sender is not the L2 messenger or owner"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumSequencerUptimeFeed.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumSequencerUptimeFeed.sol new file mode 100644 index 0000000..76d3600 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumSequencerUptimeFeed.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {AddressAliasHelper} from "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; +import {ForwarderInterface} from "./interfaces/ForwarderInterface.sol"; +import {AggregatorInterface} from "../interfaces/AggregatorInterface.sol"; +import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol"; +import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; +import {FlagsInterface} from "./interfaces/FlagsInterface.sol"; +import {ArbitrumSequencerUptimeFeedInterface} from "./interfaces/ArbitrumSequencerUptimeFeedInterface.sol"; +import {SimpleReadAccessController} from "../SimpleReadAccessController.sol"; +import {ConfirmedOwner} from "../ConfirmedOwner.sol"; + +/** + * @title ArbitrumSequencerUptimeFeed - L2 sequencer uptime status aggregator + * @notice L2 contract that receives status updates from a specific L1 address, + * records a new answer if the status changed, and raises or lowers the flag on the + * stored Flags contract. + */ +contract ArbitrumSequencerUptimeFeed is + AggregatorV2V3Interface, + ArbitrumSequencerUptimeFeedInterface, + TypeAndVersionInterface, + SimpleReadAccessController +{ + /// @dev Round info (for uptime history) + struct Round { + bool status; + uint64 timestamp; + } + + /// @dev Packed state struct to save sloads + struct FeedState { + uint80 latestRoundId; + bool latestStatus; + uint64 latestTimestamp; + } + + /// @notice Contract is not yet initialized + error Uninitialized(); + /// @notice Contract is already initialized + error AlreadyInitialized(); + /// @notice Sender is not the L2 messenger + error InvalidSender(); + /// @notice Replacement for AggregatorV3Interface "No data present" + error NoDataPresent(); + + event Initialized(); + event L1SenderTransferred(address indexed from, address indexed to); + /// @dev Emitted when an `updateStatus` call is ignored due to unchanged status or stale timestamp + event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); + + /// @dev Follows: https://eips.ethereum.org/EIPS/eip-1967 + address public constant FLAG_L2_SEQ_OFFLINE = + address(bytes20(bytes32(uint256(keccak256("chainlink.flags.arbitrum-seq-offline")) - 1))); + + uint8 public constant override decimals = 0; + string public constant override description = "L2 Sequencer Uptime Status Feed"; + uint256 public constant override version = 1; + + /// @dev Flags contract to raise/lower flags on, during status transitions + FlagsInterface public immutable FLAGS; + /// @dev L1 address + address private s_l1Sender; + /// @dev s_latestRoundId == 0 means this contract is uninitialized. + FeedState private s_feedState = FeedState({latestRoundId: 0, latestStatus: false, latestTimestamp: 0}); + mapping(uint80 => Round) private s_rounds; + + /** + * @param flagsAddress Address of the Flags contract on L2 + * @param l1SenderAddress Address of the L1 contract that is permissioned to call this contract + */ + constructor(address flagsAddress, address l1SenderAddress) { + setL1Sender(l1SenderAddress); + + FLAGS = FlagsInterface(flagsAddress); + } + + /** + * @notice Check if a roundId is valid in this current contract state + * @dev Mainly used for AggregatorV2V3Interface functions + * @param roundId Round ID to check + */ + function isValidRound(uint256 roundId) private view returns (bool) { + return roundId > 0 && roundId <= type(uint80).max && s_feedState.latestRoundId >= roundId; + } + + /// @notice Check that this contract is initialised, otherwise throw + function requireInitialized(uint80 latestRoundId) private pure { + if (latestRoundId == 0) { + revert Uninitialized(); + } + } + + /** + * @notice Initialise the first round. Can't be done in the constructor, + * because this contract's address must be permissioned by the the Flags contract + * (The Flags contract itself is a SimpleReadAccessController). + */ + function initialize() external onlyOwner { + FeedState memory feedState = s_feedState; + if (feedState.latestRoundId != 0) { + revert AlreadyInitialized(); + } + + uint64 timestamp = uint64(block.timestamp); + bool currentStatus = FLAGS.getFlag(FLAG_L2_SEQ_OFFLINE); + + // Initialise roundId == 1 as the first round + recordRound(1, currentStatus, timestamp); + + emit Initialized(); + } + + /** + * @notice versions: + * + * - ArbitrumSequencerUptimeFeed 1.0.0: initial release + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "ArbitrumSequencerUptimeFeed 1.0.0"; + } + + /// @return L1 sender address + function l1Sender() public view virtual returns (address) { + return s_l1Sender; + } + + /** + * @notice Set the allowed L1 sender for this contract to a new L1 sender + * @dev Can be disabled by setting the L1 sender as `address(0)`. Accessible only by owner. + * @param to new L1 sender that will be allowed to call `updateStatus` on this contract + */ + function transferL1Sender(address to) external virtual onlyOwner { + setL1Sender(to); + } + + /// @notice internal method that stores the L1 sender + function setL1Sender(address to) private { + address from = s_l1Sender; + if (from != to) { + s_l1Sender = to; + emit L1SenderTransferred(from, to); + } + } + + /** + * @notice Messages sent by the stored L1 sender will arrive on L2 with this + * address as the `msg.sender` + * @return L2-aliased form of the L1 sender address + */ + function aliasedL1MessageSender() public view returns (address) { + return AddressAliasHelper.applyL1ToL2Alias(l1Sender()); + } + + /** + * @dev Returns an AggregatorV2V3Interface compatible answer from status flag + * + * @param status The status flag to convert to an aggregator-compatible answer + */ + function getStatusAnswer(bool status) private pure returns (int256) { + return status ? int256(1) : int256(0); + } + + /** + * @notice Raise or lower the flag on the stored Flags contract. + */ + function forwardStatusToFlags(bool status) private { + if (status) { + FLAGS.raiseFlag(FLAG_L2_SEQ_OFFLINE); + } else { + FLAGS.lowerFlag(FLAG_L2_SEQ_OFFLINE); + } + } + + /** + * @notice Helper function to record a round and set the latest feed state. + * + * @param roundId The round ID to record + * @param status Sequencer status + * @param timestamp Block timestamp of status update + */ + function recordRound( + uint80 roundId, + bool status, + uint64 timestamp + ) private { + Round memory nextRound = Round(status, timestamp); + FeedState memory feedState = FeedState(roundId, status, timestamp); + + s_rounds[roundId] = nextRound; + s_feedState = feedState; + + emit NewRound(roundId, msg.sender, timestamp); + emit AnswerUpdated(getStatusAnswer(status), roundId, timestamp); + } + + /** + * @notice Record a new status and timestamp if it has changed since the last round. + * @dev This function will revert if not called from `l1Sender` via the L1->L2 messenger. + * + * @param status Sequencer status + * @param timestamp Block timestamp of status update + */ + function updateStatus(bool status, uint64 timestamp) external override { + FeedState memory feedState = s_feedState; + requireInitialized(feedState.latestRoundId); + if (msg.sender != aliasedL1MessageSender()) { + revert InvalidSender(); + } + + // Ignore if status did not change or latest recorded timestamp is newer + if (feedState.latestStatus == status || feedState.latestTimestamp > timestamp) { + emit UpdateIgnored(feedState.latestStatus, feedState.latestTimestamp, status, timestamp); + return; + } + + // Prepare a new round with updated status + feedState.latestRoundId += 1; + recordRound(feedState.latestRoundId, status, timestamp); + + forwardStatusToFlags(status); + } + + /// @inheritdoc AggregatorInterface + function latestAnswer() external view override checkAccess returns (int256) { + FeedState memory feedState = s_feedState; + requireInitialized(feedState.latestRoundId); + return getStatusAnswer(feedState.latestStatus); + } + + /// @inheritdoc AggregatorInterface + function latestTimestamp() external view override checkAccess returns (uint256) { + FeedState memory feedState = s_feedState; + requireInitialized(feedState.latestRoundId); + return feedState.latestTimestamp; + } + + /// @inheritdoc AggregatorInterface + function latestRound() external view override checkAccess returns (uint256) { + FeedState memory feedState = s_feedState; + requireInitialized(feedState.latestRoundId); + return feedState.latestRoundId; + } + + /// @inheritdoc AggregatorInterface + function getAnswer(uint256 roundId) external view override checkAccess returns (int256) { + requireInitialized(s_feedState.latestRoundId); + if (isValidRound(roundId)) { + return getStatusAnswer(s_rounds[uint80(roundId)].status); + } + + return 0; + } + + /// @inheritdoc AggregatorInterface + function getTimestamp(uint256 roundId) external view override checkAccess returns (uint256) { + requireInitialized(s_feedState.latestRoundId); + if (isValidRound(roundId)) { + return s_rounds[uint80(roundId)].timestamp; + } + + return 0; + } + + /// @inheritdoc AggregatorV3Interface + function getRoundData(uint80 _roundId) + public + view + override + checkAccess + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + requireInitialized(s_feedState.latestRoundId); + + if (isValidRound(_roundId)) { + Round memory round = s_rounds[_roundId]; + answer = getStatusAnswer(round.status); + startedAt = uint256(round.timestamp); + } else { + answer = 0; + startedAt = 0; + } + roundId = _roundId; + updatedAt = startedAt; + answeredInRound = roundId; + } + + /// @inheritdoc AggregatorV3Interface + function latestRoundData() + external + view + override + checkAccess + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + FeedState memory feedState = s_feedState; + requireInitialized(feedState.latestRoundId); + + roundId = feedState.latestRoundId; + answer = getStatusAnswer(feedState.latestStatus); + startedAt = feedState.latestTimestamp; + updatedAt = startedAt; + answeredInRound = roundId; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumValidator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumValidator.sol new file mode 100644 index 0000000..472ec06 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ArbitrumValidator.sol @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/AggregatorValidatorInterface.sol"; +import "../interfaces/TypeAndVersionInterface.sol"; +import "../interfaces/AccessControllerInterface.sol"; +import "../interfaces/AggregatorV3Interface.sol"; +import "../SimpleWriteAccessController.sol"; + +/* ./dev dependencies - to be moved from ./dev after audit */ +import "./interfaces/ArbitrumSequencerUptimeFeedInterface.sol"; +import "./interfaces/FlagsInterface.sol"; +import "./interfaces/IArbitrumDelayedInbox.sol"; +import "./vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; +import "./vendor/arb-os/e8d9696f21/contracts/arbos/builtin/ArbSys.sol"; +import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; + +/** + * @title ArbitrumValidator - makes xDomain L2 Flags contract call (using L2 xDomain Forwarder contract) + * @notice Allows to raise and lower Flags on the Arbitrum L2 network through L1 bridge + * - The internal AccessController controls the access of the validate method + * - Gas configuration is controlled by a configurable external SimpleWriteAccessController + * - Funds on the contract are managed by the owner + */ +contract ArbitrumValidator is TypeAndVersionInterface, AggregatorValidatorInterface, SimpleWriteAccessController { + enum PaymentStrategy { + L1, + L2 + } + // Config for L1 -> L2 Arbitrum retryable ticket message + struct GasConfig { + uint256 maxGas; + uint256 gasPriceBid; + uint256 baseFee; // Will use block.baseFee if set to 0 + address gasPriceL1FeedAddr; + } + + /// @dev Precompiled contract that exists in every Arbitrum chain at address(100). Exposes a variety of system-level functionality. + address constant ARBSYS_ADDR = address(0x0000000000000000000000000000000000000064); + + int256 private constant ANSWER_SEQ_OFFLINE = 1; + + /// @notice The address of Arbitrum's DelayedInbox + address public immutable CROSS_DOMAIN_MESSENGER; + address public immutable L2_SEQ_STATUS_RECORDER; + // L2 xDomain alias address of this contract + address public immutable L2_ALIAS = AddressAliasHelper.applyL1ToL2Alias(address(this)); + + PaymentStrategy private s_paymentStrategy; + GasConfig private s_gasConfig; + AccessControllerInterface private s_configAC; + + /** + * @notice emitted when a new payment strategy is set + * @param paymentStrategy strategy describing how the contract pays for xDomain calls + */ + event PaymentStrategySet(PaymentStrategy indexed paymentStrategy); + + /** + * @notice emitted when a new gas configuration is set + * @param maxGas gas limit for immediate L2 execution attempt. + * @param gasPriceBid maximum L2 gas price to pay + * @param gasPriceL1FeedAddr address of the L1 gas price feed (used to approximate Arbitrum retryable ticket submission cost) + */ + event GasConfigSet(uint256 maxGas, uint256 gasPriceBid, address indexed gasPriceL1FeedAddr); + + /** + * @notice emitted when a new gas access-control contract is set + * @param previous the address prior to the current setting + * @param current the address of the new access-control contract + */ + event ConfigACSet(address indexed previous, address indexed current); + + /** + * @notice emitted when a new ETH withdrawal from L2 was requested + * @param id unique id of the published retryable transaction (keccak256(requestID, uint(0)) + * @param amount of funds to withdraw + */ + event L2WithdrawalRequested(uint256 indexed id, uint256 amount, address indexed refundAddr); + + /** + * @param crossDomainMessengerAddr address the xDomain bridge messenger (Arbitrum Inbox L1) contract address + * @param l2ArbitrumSequencerUptimeFeedAddr the L2 Flags contract address + * @param configACAddr address of the access controller for managing gas price on Arbitrum + * @param maxGas gas limit for immediate L2 execution attempt. A value around 1M should be sufficient + * @param gasPriceBid maximum L2 gas price to pay + * @param gasPriceL1FeedAddr address of the L1 gas price feed (used to approximate Arbitrum retryable ticket submission cost) + * @param paymentStrategy strategy describing how the contract pays for xDomain calls + */ + constructor( + address crossDomainMessengerAddr, + address l2ArbitrumSequencerUptimeFeedAddr, + address configACAddr, + uint256 maxGas, + uint256 gasPriceBid, + uint256 baseFee, + address gasPriceL1FeedAddr, + PaymentStrategy paymentStrategy + ) { + require(crossDomainMessengerAddr != address(0), "Invalid xDomain Messenger address"); + require(l2ArbitrumSequencerUptimeFeedAddr != address(0), "Invalid ArbitrumSequencerUptimeFeed contract address"); + CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; + L2_SEQ_STATUS_RECORDER = l2ArbitrumSequencerUptimeFeedAddr; + // Additional L2 payment configuration + _setConfigAC(configACAddr); + _setGasConfig(maxGas, gasPriceBid, baseFee, gasPriceL1FeedAddr); + _setPaymentStrategy(paymentStrategy); + } + + /** + * @notice versions: + * + * - ArbitrumValidator 0.1.0: initial release + * - ArbitrumValidator 0.2.0: critical Arbitrum network update + * - xDomain `msg.sender` backwards incompatible change (now an alias address) + * - new `withdrawFundsFromL2` fn that withdraws from L2 xDomain alias address + * - approximation of `maxSubmissionCost` using a L1 gas price feed + * - ArbitrumValidator 1.0.0: change target of L2 sequencer status update + * - now calls `updateStatus` on an L2 ArbitrumSequencerUptimeFeed contract instead of + * directly calling the Flags contract + * - ArbitrumValidator 2.0.0: change how maxSubmissionCost is calculated when sending cross chain messages + * - now calls `calculateRetryableSubmissionFee` instead of inlining equation to estimate + * the maxSubmissionCost required to send the message to L2 + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "ArbitrumValidator 2.0.0"; + } + + /// @return stored PaymentStrategy + function paymentStrategy() external view virtual returns (PaymentStrategy) { + return s_paymentStrategy; + } + + /// @return stored GasConfig + function gasConfig() external view virtual returns (GasConfig memory) { + return s_gasConfig; + } + + /// @return config AccessControllerInterface contract address + function configAC() external view virtual returns (address) { + return address(s_configAC); + } + + /** + * @notice makes this contract payable + * @dev receives funds: + * - to use them (if configured) to pay for L2 execution on L1 + * - when withdrawing funds from L2 xDomain alias address (pay for L2 execution on L2) + */ + receive() external payable {} + + /** + * @notice withdraws all funds available in this contract to the msg.sender + * @dev only owner can call this + */ + function withdrawFunds() external onlyOwner { + address payable recipient = payable(msg.sender); + uint256 amount = address(this).balance; + Address.sendValue(recipient, amount); + } + + /** + * @notice withdraws all funds available in this contract to the address specified + * @dev only owner can call this + * @param recipient address where to send the funds + */ + function withdrawFundsTo(address payable recipient) external onlyOwner { + uint256 amount = address(this).balance; + Address.sendValue(recipient, amount); + } + + /** + * @notice withdraws funds from L2 xDomain alias address (representing this L1 contract) + * @dev only owner can call this + * @param amount of funds to withdraws + * @param refundAddr address where gas excess on L2 will be sent + * WARNING: `refundAddr` is not aliased! Make sure you can recover the refunded funds on L2. + * @return id unique id of the published retryable transaction (keccak256(requestID, uint(0)) + */ + function withdrawFundsFromL2(uint256 amount, address refundAddr) external onlyOwner returns (uint256 id) { + // Build an xDomain message to trigger the ArbSys precompile, which will create a L2 -> L1 tx transferring `amount` + bytes memory message = abi.encodeWithSelector(ArbSys.withdrawEth.selector, address(this)); + // Make the xDomain call + // NOTICE: We approximate the max submission cost of sending a retryable tx with specific calldata length. + uint256 maxSubmissionCost = _approximateMaxSubmissionCost(message.length); + uint256 maxGas = 120_000; // static `maxGas` for L2 -> L1 transfer + uint256 gasPriceBid = s_gasConfig.gasPriceBid; + uint256 l1PaymentValue = s_paymentStrategy == PaymentStrategy.L1 + ? _maxRetryableTicketCost(maxSubmissionCost, maxGas, gasPriceBid) + : 0; + // NOTICE: In the case of PaymentStrategy.L2 the L2 xDomain alias address needs to be funded, as it will be paying the fee. + id = IArbitrumDelayedInbox(CROSS_DOMAIN_MESSENGER).createRetryableTicketNoRefundAliasRewrite{value: l1PaymentValue}( + ARBSYS_ADDR, // target + amount, // L2 call value (requested) + maxSubmissionCost, + refundAddr, // excessFeeRefundAddress + refundAddr, // callValueRefundAddress + maxGas, + gasPriceBid, + message + ); + emit L2WithdrawalRequested(id, amount, refundAddr); + } + + /** + * @notice sets config AccessControllerInterface contract + * @dev only owner can call this + * @param accessController new AccessControllerInterface contract address + */ + function setConfigAC(address accessController) external onlyOwner { + _setConfigAC(accessController); + } + + /** + * @notice sets Arbitrum gas configuration + * @dev access control provided by `configAC` + * @param maxGas gas limit for immediate L2 execution attempt. A value around 1M should be sufficient + * @param gasPriceBid maximum L2 gas price to pay + * @param gasPriceL1FeedAddr address of the L1 gas price feed (used to approximate Arbitrum retryable ticket submission cost) + */ + function setGasConfig( + uint256 maxGas, + uint256 gasPriceBid, + uint256 baseFee, + address gasPriceL1FeedAddr + ) external onlyOwnerOrConfigAccess { + _setGasConfig(maxGas, gasPriceBid, baseFee, gasPriceL1FeedAddr); + } + + /** + * @notice sets the payment strategy + * @dev access control provided by `configAC` + * @param paymentStrategy strategy describing how the contract pays for xDomain calls + */ + function setPaymentStrategy(PaymentStrategy paymentStrategy) external onlyOwnerOrConfigAccess { + _setPaymentStrategy(paymentStrategy); + } + + /** + * @notice validate method sends an xDomain L2 tx to update Flags contract, in case of change from `previousAnswer`. + * @dev A retryable ticket is created on the Arbitrum L1 Inbox contract. The tx gas fee can be paid from this + * contract providing a value, or if no L1 value is sent with the xDomain message the gas will be paid by + * the L2 xDomain alias account (generated from `address(this)`). This method is accessed controlled. + * @param previousAnswer previous aggregator answer + * @param currentAnswer new aggregator answer - value of 1 considers the service offline. + */ + function validate( + uint256, /* previousRoundId */ + int256 previousAnswer, + uint256, /* currentRoundId */ + int256 currentAnswer + ) external override checkAccess returns (bool) { + // Avoids resending to L2 the same tx on every call + if (previousAnswer == currentAnswer) { + return true; + } + + // Excess gas on L2 will be sent to the L2 xDomain alias address of this contract + address refundAddr = L2_ALIAS; + // Encode the ArbitrumSequencerUptimeFeed call + bytes4 selector = ArbitrumSequencerUptimeFeedInterface.updateStatus.selector; + bool status = currentAnswer == ANSWER_SEQ_OFFLINE; + uint64 timestamp = uint64(block.timestamp); + // Encode `status` and `timestamp` + bytes memory message = abi.encodeWithSelector(selector, status, timestamp); + // Make the xDomain call + // NOTICE: We approximate the max submission cost of sending a retryable tx with specific calldata length. + uint256 maxSubmissionCost = _approximateMaxSubmissionCost(message.length); + uint256 maxGas = s_gasConfig.maxGas; + uint256 gasPriceBid = s_gasConfig.gasPriceBid; + uint256 l1PaymentValue = s_paymentStrategy == PaymentStrategy.L1 + ? _maxRetryableTicketCost(maxSubmissionCost, maxGas, gasPriceBid) + : 0; + // NOTICE: In the case of PaymentStrategy.L2 the L2 xDomain alias address needs to be funded, as it will be paying the fee. + // We also ignore the returned msg number, that can be queried via the `InboxMessageDelivered` event. + IArbitrumDelayedInbox(CROSS_DOMAIN_MESSENGER).createRetryableTicketNoRefundAliasRewrite{value: l1PaymentValue}( + L2_SEQ_STATUS_RECORDER, // target + 0, // L2 call value + maxSubmissionCost, + refundAddr, // excessFeeRefundAddress + refundAddr, // callValueRefundAddress + maxGas, + gasPriceBid, + message + ); + // return success + return true; + } + + /// @notice internal method that stores the payment strategy + function _setPaymentStrategy(PaymentStrategy paymentStrategy) internal { + s_paymentStrategy = paymentStrategy; + emit PaymentStrategySet(paymentStrategy); + } + + /// @notice internal method that stores the gas configuration + function _setGasConfig( + uint256 maxGas, + uint256 gasPriceBid, + uint256 baseFee, + address gasPriceL1FeedAddr + ) internal { + require(maxGas > 0, "Max gas is zero"); + require(gasPriceBid > 0, "Gas price bid is zero"); + require(gasPriceL1FeedAddr != address(0), "Gas price Aggregator is zero address"); + s_gasConfig = GasConfig(maxGas, gasPriceBid, baseFee, gasPriceL1FeedAddr); + emit GasConfigSet(maxGas, gasPriceBid, gasPriceL1FeedAddr); + } + + /// @notice Internal method that stores the configuration access controller + function _setConfigAC(address accessController) internal { + address previousAccessController = address(s_configAC); + if (accessController != previousAccessController) { + s_configAC = AccessControllerInterface(accessController); + emit ConfigACSet(previousAccessController, accessController); + } + } + + /** + * @notice Internal method that approximates the `maxSubmissionCost` + * @dev This function estimates the max submission cost using the formula + * implemented in Arbitrum DelayedInbox's calculateRetryableSubmissionFee function + * @param calldataSizeInBytes xDomain message size in bytes + */ + function _approximateMaxSubmissionCost(uint256 calldataSizeInBytes) internal view returns (uint256) { + return + IArbitrumDelayedInbox(CROSS_DOMAIN_MESSENGER).calculateRetryableSubmissionFee( + calldataSizeInBytes, + s_gasConfig.baseFee + ); + } + + /// @notice Internal helper method that calculates the total cost of the xDomain retryable ticket call + function _maxRetryableTicketCost( + uint256 maxSubmissionCost, + uint256 maxGas, + uint256 gasPriceBid + ) internal pure returns (uint256) { + return maxSubmissionCost + maxGas * gasPriceBid; + } + + /// @dev reverts if the caller does not have access to change the configuration + modifier onlyOwnerOrConfigAccess() { + require( + msg.sender == owner() || (address(s_configAC) != address(0) && s_configAC.hasAccess(msg.sender, msg.data)), + "No access" + ); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CanaryUpkeep.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CanaryUpkeep.sol new file mode 100644 index 0000000..5226c04 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CanaryUpkeep.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "../interfaces/KeeperCompatibleInterface.sol"; +import "../interfaces/KeeperRegistryInterface.sol"; +import "../ConfirmedOwner.sol"; + +error NoKeeperNodes(); +error InsufficientInterval(); + +/** + * @notice A canary upkeep which requires a different keeper to service its upkeep at an interval. This makes sure that + * all keepers are in a healthy state. + */ +contract CanaryUpkeep is KeeperCompatibleInterface, ConfirmedOwner { + uint256 private s_keeperIndex; + uint256 private s_interval; + uint256 private s_timestamp; + KeeperRegistryExecutableInterface private immutable i_keeperRegistry; + + /** + * @param keeperRegistry address of a keeper registry + */ + constructor(KeeperRegistryExecutableInterface keeperRegistry, uint256 interval) ConfirmedOwner(msg.sender) { + i_keeperRegistry = keeperRegistry; + s_timestamp = block.timestamp; + s_interval = interval; + s_keeperIndex = 0; + } + + /** + * @return the current keeper index + */ + function getKeeperIndex() external view returns (uint256) { + return s_keeperIndex; + } + + /** + * @return the current timestamp + */ + function getTimestamp() external view returns (uint256) { + return s_timestamp; + } + + /** + * @return the current interval + */ + function getInterval() external view returns (uint256) { + return s_interval; + } + + /** + * @return the keeper registry + */ + function getKeeperRegistry() external view returns (KeeperRegistryExecutableInterface) { + return i_keeperRegistry; + } + + /** + * @notice updates the interval + * @param interval the new interval + */ + function setInterval(uint256 interval) external onlyOwner { + s_interval = interval; + } + + /** + * @notice returns true if keeper array is not empty and sufficient time has passed + */ + function checkUpkeep( + bytes calldata /* checkData */ + ) external view override returns (bool, bytes memory) { + bool upkeepNeeded = block.timestamp >= s_interval + s_timestamp; + return (upkeepNeeded, bytes("")); + } + + /** + * @notice checks keepers array limit, timestamp limit, and requires transaction origin must be the anticipated keeper. + * If all checks pass, update the keeper index and timestamp. Otherwise, revert this transaction. + */ + function performUpkeep( + bytes calldata /* performData */ + ) external override { + (State memory _s, Config memory _c, address[] memory keepers) = i_keeperRegistry.getState(); + if (keepers.length == 0) { + revert NoKeeperNodes(); + } + if (block.timestamp < s_interval + s_timestamp) { + revert InsufficientInterval(); + } + // if keepers array is shortened, this statement will make sure keeper index is always valid + if (s_keeperIndex >= keepers.length) { + s_keeperIndex = 0; + } + + require(tx.origin == keepers[s_keeperIndex], "transaction origin is not the anticipated keeper."); + s_keeperIndex = (s_keeperIndex + 1) % keepers.length; + s_timestamp = block.timestamp; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CanaryUpkeep1_2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CanaryUpkeep1_2.sol new file mode 100644 index 0000000..5a0b464 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CanaryUpkeep1_2.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "../interfaces/KeeperCompatibleInterface.sol"; +import "../interfaces/KeeperRegistryInterface1_2.sol"; +import "../ConfirmedOwner.sol"; + +error NoKeeperNodes(); +error InsufficientInterval(); + +/** + * @notice A canary upkeep which requires a different keeper to service its upkeep at an interval. This makes sure that + * all keepers are in a healthy state. + */ +contract CanaryUpkeep1_2 is KeeperCompatibleInterface, ConfirmedOwner { + uint256 private s_keeperIndex; + uint256 private s_interval; + uint256 private s_timestamp; + KeeperRegistryExecutableInterface private immutable i_keeperRegistry; + + /** + * @param keeperRegistry address of a keeper registry + */ + constructor(KeeperRegistryExecutableInterface keeperRegistry, uint256 interval) ConfirmedOwner(msg.sender) { + i_keeperRegistry = keeperRegistry; + s_timestamp = block.timestamp; + s_interval = interval; + s_keeperIndex = 0; + } + + /** + * @return the current keeper index + */ + function getKeeperIndex() external view returns (uint256) { + return s_keeperIndex; + } + + /** + * @return the current timestamp + */ + function getTimestamp() external view returns (uint256) { + return s_timestamp; + } + + /** + * @return the current interval + */ + function getInterval() external view returns (uint256) { + return s_interval; + } + + /** + * @return the keeper registry + */ + function getKeeperRegistry() external view returns (KeeperRegistryExecutableInterface) { + return i_keeperRegistry; + } + + /** + * @notice updates the interval + * @param interval the new interval + */ + function setInterval(uint256 interval) external onlyOwner { + s_interval = interval; + } + + /** + * @notice returns true if keeper array is not empty and sufficient time has passed + */ + function checkUpkeep( + bytes calldata /* checkData */ + ) external view override returns (bool, bytes memory) { + bool upkeepNeeded = block.timestamp >= s_interval + s_timestamp; + return (upkeepNeeded, bytes("")); + } + + /** + * @notice checks keepers array limit, timestamp limit, and requires transaction origin must be the anticipated keeper. + * If all checks pass, update the keeper index and timestamp. Otherwise, revert this transaction. + */ + function performUpkeep( + bytes calldata /* performData */ + ) external override { + (State memory _s, Config memory _c, address[] memory keepers) = i_keeperRegistry.getState(); + if (keepers.length == 0) { + revert NoKeeperNodes(); + } + if (block.timestamp < s_interval + s_timestamp) { + revert InsufficientInterval(); + } + // if keepers array is shortened, this statement will make sure keeper index is always valid + if (s_keeperIndex >= keepers.length) { + s_keeperIndex = 0; + } + + require(tx.origin == keepers[s_keeperIndex], "transaction origin is not the anticipated keeper."); + s_keeperIndex = (s_keeperIndex + 1) % keepers.length; + s_timestamp = block.timestamp; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Chainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Chainlink.sol new file mode 100644 index 0000000..2f6e596 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Chainlink.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { CBORChainlink } from "../vendor/CBORChainlink.sol"; +import { BufferChainlink } from "../vendor/BufferChainlink.sol"; + +/** + * @title Library for common Chainlink functions + * @dev Uses imported CBOR library for encoding to buffer + */ +library Chainlink { + uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase + + using CBORChainlink for BufferChainlink.buffer; + + struct Request { + bytes32 id; + address callbackAddress; + bytes4 callbackFunctionId; + uint256 nonce; + BufferChainlink.buffer buf; + } + + /** + * @notice Initializes a Chainlink request + * @dev Sets the ID, callback address, and callback function signature on the request + * @param self The uninitialized request + * @param jobId The Job Specification ID + * @param callbackAddr The callback address + * @param callbackFunc The callback function signature + * @return The initialized request + */ + function initialize( + Request memory self, + bytes32 jobId, + address callbackAddr, + bytes4 callbackFunc + ) + internal + pure + returns ( + Chainlink.Request memory + ) + { + BufferChainlink.init(self.buf, defaultBufferSize); + self.id = jobId; + self.callbackAddress = callbackAddr; + self.callbackFunctionId = callbackFunc; + return self; + } + + /** + * @notice Sets the data for the buffer without encoding CBOR on-chain + * @dev CBOR can be closed with curly-brackets {} or they can be left off + * @param self The initialized request + * @param data The CBOR data + */ + function setBuffer( + Request memory self, + bytes memory data + ) + internal + pure + { + BufferChainlink.init(self.buf, data.length); + BufferChainlink.append(self.buf, data); + } + + /** + * @notice Adds a string value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The string value to add + */ + function add( + Request memory self, + string memory key, + string memory value + ) + internal + pure + { + self.buf.encodeString(key); + self.buf.encodeString(value); + } + + /** + * @notice Adds a bytes value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The bytes value to add + */ + function addBytes( + Request memory self, + string memory key, + bytes memory value + ) + internal + pure + { + self.buf.encodeString(key); + self.buf.encodeBytes(value); + } + + /** + * @notice Adds a int256 value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The int256 value to add + */ + function addInt( + Request memory self, + string memory key, + int256 value + ) + internal + pure + { + self.buf.encodeString(key); + self.buf.encodeInt(value); + } + + /** + * @notice Adds a uint256 value to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param value The uint256 value to add + */ + function addUint( + Request memory self, + string memory key, + uint256 value + ) + internal + pure + { + self.buf.encodeString(key); + self.buf.encodeUInt(value); + } + + /** + * @notice Adds an array of strings to the request with a given key name + * @param self The initialized request + * @param key The name of the key + * @param values The array of string values to add + */ + function addStringArray( + Request memory self, + string memory key, + string[] memory values + ) + internal + pure + { + self.buf.encodeString(key); + self.buf.startArray(); + for (uint256 i = 0; i < values.length; i++) { + self.buf.encodeString(values[i]); + } + self.buf.endSequence(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ChainlinkClient.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ChainlinkClient.sol new file mode 100644 index 0000000..07d1f96 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ChainlinkClient.sol @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Chainlink.sol"; +import "../interfaces/ENSInterface.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/OperatorInterface.sol"; +import "../interfaces/PointerInterface.sol"; +import { ENSResolver as ENSResolver_Chainlink } from "../vendor/ENSResolver.sol"; + +/** + * @title The ChainlinkClient contract + * @notice Contract writers can inherit this contract in order to create requests for the + * Chainlink network + */ +contract ChainlinkClient { + using Chainlink for Chainlink.Request; + + uint256 constant internal LINK_DIVISIBILITY = 10**18; + uint256 constant private AMOUNT_OVERRIDE = 0; + address constant private SENDER_OVERRIDE = address(0); + uint256 constant private ORACLE_ARGS_VERSION = 1; + uint256 constant private OPERATOR_ARGS_VERSION = 2; + bytes32 constant private ENS_TOKEN_SUBNAME = keccak256("link"); + bytes32 constant private ENS_ORACLE_SUBNAME = keccak256("oracle"); + address constant private LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571; + + ENSInterface private ens; + bytes32 private ensNode; + LinkTokenInterface private link; + OperatorInterface private oracle; + uint256 private requestCount = 1; + mapping(bytes32 => address) private pendingRequests; + + event ChainlinkRequested( + bytes32 indexed id + ); + event ChainlinkFulfilled( + bytes32 indexed id + ); + event ChainlinkCancelled( + bytes32 indexed id + ); + + /** + * @notice Creates a request that can hold additional parameters + * @param specId The Job Specification ID that the request will be created for + * @param callbackAddress The callback address that the response will be sent to + * @param callbackFunctionSignature The callback function signature to use for the callback address + * @return A Chainlink Request struct in memory + */ + function buildChainlinkRequest( + bytes32 specId, + address callbackAddress, + bytes4 callbackFunctionSignature + ) + internal + pure + returns ( + Chainlink.Request memory + ) + { + Chainlink.Request memory req; + return req.initialize(specId, callbackAddress, callbackFunctionSignature); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev Calls `chainlinkRequestTo` with the stored oracle address + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequest( + Chainlink.Request memory req, + uint256 payment + ) + internal + returns ( + bytes32 + ) + { + return sendChainlinkRequestTo(address(oracle), req, payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function sendChainlinkRequestTo( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment + ) + internal + returns ( + bytes32 requestId + ) + { + return rawRequest(oracleAddress, req, payment, ORACLE_ARGS_VERSION, oracle.oracleRequest.selector); + } + + /** + * @notice Creates a Chainlink request to the stored oracle address + * @dev This function supports multi-word response + * @dev Calls `requestOracleDataFrom` with the stored oracle address + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function requestOracleData( + Chainlink.Request memory req, + uint256 payment + ) + internal + returns ( + bytes32 + ) + { + return requestOracleDataFrom(address(oracle), req, payment); + } + + /** + * @notice Creates a Chainlink request to the specified oracle address + * @dev This function supports multi-word response + * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to + * send LINK which creates a request on the target oracle contract. + * Emits ChainlinkRequested event. + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @return requestId The request ID + */ + function requestOracleDataFrom( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment + ) + internal + returns ( + bytes32 requestId + ) + { + return rawRequest(oracleAddress, req, payment, OPERATOR_ARGS_VERSION, oracle.requestOracleData.selector); + } + + /** + * @notice Make a request to an oracle + * @param oracleAddress The address of the oracle for the request + * @param req The initialized Chainlink Request + * @param payment The amount of LINK to send for the request + * @param argsVersion The version of data support (single word, multi word) + * @return requestId The request ID + */ + function rawRequest( + address oracleAddress, + Chainlink.Request memory req, + uint256 payment, + uint256 argsVersion, + bytes4 funcSelector + ) + private + returns ( + bytes32 requestId + ) + { + requestId = keccak256(abi.encodePacked(this, requestCount)); + req.nonce = requestCount; + pendingRequests[requestId] = oracleAddress; + emit ChainlinkRequested(requestId); + bytes memory encodedData = abi.encodeWithSelector( + funcSelector, + SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address + AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent + req.id, + req.callbackAddress, + req.callbackFunctionId, + req.nonce, + argsVersion, + req.buf.buf); + require(link.transferAndCall(oracleAddress, payment, encodedData), "unable to transferAndCall to oracle"); + requestCount += 1; + } + + /** + * @notice Allows a request to be cancelled if it has not been fulfilled + * @dev Requires keeping track of the expiration value emitted from the oracle contract. + * Deletes the request from the `pendingRequests` mapping. + * Emits ChainlinkCancelled event. + * @param requestId The request ID + * @param payment The amount of LINK sent for the request + * @param callbackFunc The callback function specified for the request + * @param expiration The time of the expiration for the request + */ + function cancelChainlinkRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunc, + uint256 expiration + ) + internal + { + OperatorInterface requested = OperatorInterface(pendingRequests[requestId]); + delete pendingRequests[requestId]; + emit ChainlinkCancelled(requestId); + requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration); + } + + /** + * @notice Sets the stored oracle address + * @param oracleAddress The address of the oracle contract + */ + function setChainlinkOracle( + address oracleAddress + ) + internal + { + oracle = OperatorInterface(oracleAddress); + } + + /** + * @notice Sets the LINK token address + * @param linkAddress The address of the LINK token contract + */ + function setChainlinkToken( + address linkAddress + ) + internal + { + link = LinkTokenInterface(linkAddress); + } + + /** + * @notice Sets the Chainlink token address for the public + * network as given by the Pointer contract + */ + function setPublicChainlinkToken() + internal + { + setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress()); + } + + /** + * @notice Retrieves the stored address of the LINK token + * @return The address of the LINK token + */ + function chainlinkTokenAddress() + internal + view + returns ( + address + ) + { + return address(link); + } + + /** + * @notice Retrieves the stored address of the oracle contract + * @return The address of the oracle contract + */ + function chainlinkOracleAddress() + internal + view + returns ( + address + ) + { + return address(oracle); + } + + /** + * @notice Allows for a request which was created on another contract to be fulfilled + * on this contract + * @param oracleAddress The address of the oracle contract that will fulfill the request + * @param requestId The request ID used for the response + */ + function addChainlinkExternalRequest( + address oracleAddress, + bytes32 requestId + ) + internal + notPendingRequest(requestId) + { + pendingRequests[requestId] = oracleAddress; + } + + /** + * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS + * @dev Accounts for subnodes having different resolvers + * @param ensAddress The address of the ENS contract + * @param node The ENS node hash + */ + function useChainlinkWithENS( + address ensAddress, + bytes32 node + ) + internal + { + ens = ENSInterface(ensAddress); + ensNode = node; + bytes32 linkSubnode = keccak256(abi.encodePacked(ensNode, ENS_TOKEN_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(linkSubnode)); + setChainlinkToken(resolver.addr(linkSubnode)); + updateChainlinkOracleWithENS(); + } + + /** + * @notice Sets the stored oracle contract with the address resolved by ENS + * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously + */ + function updateChainlinkOracleWithENS() + internal + { + bytes32 oracleSubnode = keccak256(abi.encodePacked(ensNode, ENS_ORACLE_SUBNAME)); + ENSResolver_Chainlink resolver = ENSResolver_Chainlink(ens.resolver(oracleSubnode)); + setChainlinkOracle(resolver.addr(oracleSubnode)); + } + + /** + * @notice Ensures that the fulfillment is valid for this contract + * @dev Use if the contract developer prefers methods instead of modifiers for validation + * @param requestId The request ID for fulfillment + */ + function validateChainlinkCallback( + bytes32 requestId + ) + internal + recordChainlinkFulfillment(requestId) + // solhint-disable-next-line no-empty-blocks + {} + + /** + * @dev Reverts if the sender is not the oracle of the request. + * Emits ChainlinkFulfilled event. + * @param requestId The request ID for fulfillment + */ + modifier recordChainlinkFulfillment( + bytes32 requestId + ) + { + require(msg.sender == pendingRequests[requestId], + "Source must be the oracle of the request"); + delete pendingRequests[requestId]; + emit ChainlinkFulfilled(requestId); + _; + } + + /** + * @dev Reverts if the request is already pending + * @param requestId The request ID for fulfillment + */ + modifier notPendingRequest( + bytes32 requestId + ) + { + require(pendingRequests[requestId] == address(0), "Request is already pending"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ConfirmedOwner.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ConfirmedOwner.sol new file mode 100644 index 0000000..3fd2853 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ConfirmedOwner.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title The ConfirmedOwner contract + * @notice A contract with helpers for basic contract ownership. + */ +contract ConfirmedOwner { + + address private s_owner; + address private s_pendingOwner; + + event OwnershipTransferRequested( + address indexed from, + address indexed to + ); + event OwnershipTransferred( + address indexed from, + address indexed to + ); + + constructor(address newOwner) { + s_owner = newOwner; + } + + /** + * @notice Allows an owner to begin transferring ownership to a new address, + * pending. + */ + function transferOwnership( + address to + ) + external + onlyOwner() + { + require(to != msg.sender, "Cannot transfer to self"); + + s_pendingOwner = to; + + emit OwnershipTransferRequested(s_owner, to); + } + + /** + * @notice Allows an ownership transfer to be completed by the recipient. + */ + function acceptOwnership() + external + { + require(msg.sender == s_pendingOwner, "Must be proposed owner"); + + address oldOwner = s_owner; + s_owner = msg.sender; + s_pendingOwner = address(0); + + emit OwnershipTransferred(oldOwner, msg.sender); + } + + /** + * @notice Get the current owner + */ + function owner() + public + view + returns ( + address + ) + { + return s_owner; + } + + /** + * @notice Reverts if called by anyone other than the contract owner. + */ + modifier onlyOwner() { + require(msg.sender == s_owner, "Only callable by owner"); + _; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainDelegateForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainDelegateForwarder.sol new file mode 100644 index 0000000..a4c912b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainDelegateForwarder.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./CrossDomainForwarder.sol"; +import "./interfaces/ForwarderInterface.sol"; +import "./interfaces/DelegateForwarderInterface.sol"; + +/** + * @title CrossDomainDelegateForwarder - L1 xDomain account representation (with delegatecall support) + * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. + * @dev Any other L2 contract which uses this contract's address as a privileged position, + * can consider that position to be held by the `l1Owner` + */ +abstract contract CrossDomainDelegateForwarder is DelegateForwarderInterface, CrossDomainOwnable { + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainForwarder.sol new file mode 100644 index 0000000..4377c2c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainForwarder.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./CrossDomainOwnable.sol"; +import "./interfaces/ForwarderInterface.sol"; + +/** + * @title CrossDomainForwarder - L1 xDomain account representation + * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. + * @dev Any other L2 contract which uses this contract's address as a privileged position, + * can consider that position to be held by the `l1Owner` + */ +abstract contract CrossDomainForwarder is ForwarderInterface, CrossDomainOwnable { + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainOwnable.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainOwnable.sol new file mode 100644 index 0000000..cd5f309 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/CrossDomainOwnable.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../ConfirmedOwner.sol"; +import "./interfaces/CrossDomainOwnableInterface.sol"; + +/** + * @title The CrossDomainOwnable contract + * @notice A contract with helpers for cross-domain contract ownership. + */ +contract CrossDomainOwnable is CrossDomainOwnableInterface, ConfirmedOwner { + address internal s_l1Owner; + address internal s_l1PendingOwner; + + constructor(address newl1Owner) ConfirmedOwner(msg.sender) { + _setL1Owner(newl1Owner); + } + + /** + * @notice transfer ownership of this account to a new L1 owner + * @param to new L1 owner that will be allowed to call the forward fn + */ + function transferL1Ownership(address to) public virtual override onlyL1Owner { + _transferL1Ownership(to); + } + + /** + * @notice accept ownership of this account to a new L1 owner + */ + function acceptL1Ownership() public virtual override onlyProposedL1Owner { + _setL1Owner(s_l1PendingOwner); + } + + /** + * @notice Get the current owner + */ + function l1Owner() public view override returns (address) { + return s_l1Owner; + } + + /** + * @notice validate, transfer ownership, and emit relevant events + */ + function _transferL1Ownership(address to) internal { + require(to != msg.sender, "Cannot transfer to self"); + + s_l1PendingOwner = to; + + emit L1OwnershipTransferRequested(s_l1Owner, to); + } + + /** + * @notice set ownership, emit relevant events. Used in acceptOwnership() + */ + function _setL1Owner(address to) internal { + address oldOwner = s_l1Owner; + s_l1Owner = to; + s_l1PendingOwner = address(0); + + emit L1OwnershipTransferred(oldOwner, to); + } + + /** + * @notice Reverts if called by anyone other than the L1 owner. + */ + modifier onlyL1Owner() virtual { + require(msg.sender == s_l1Owner, "Only callable by L1 owner"); + _; + } + + /** + * @notice Reverts if called by anyone other than the L1 owner. + */ + modifier onlyProposedL1Owner() virtual { + require(msg.sender == s_l1PendingOwner, "Only callable by proposed L1 owner"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Denominations.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Denominations.sol new file mode 100644 index 0000000..c197b44 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Denominations.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +library Denominations { + address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; + + // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217 + address public constant USD = address(840); + address public constant GBP = address(826); + address public constant EUR = address(978); + address public constant JPY = address(392); + address public constant KRW = address(410); + address public constant CNY = address(156); + address public constant AUD = address(36); + address public constant CAD = address(124); + address public constant CHF = address(756); + address public constant ARS = address(32); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/DerivedPriceFeed.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/DerivedPriceFeed.sol new file mode 100644 index 0000000..4c07963 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/DerivedPriceFeed.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "../interfaces/AggregatorV3Interface.sol"; + +/** + * Network: Fantom Testnet + * Base: LINK/USD + * Base Address: 0x6d5689Ad4C1806D1BA0c70Ab95ebe0Da6B204fC5 + * Quote: FTM/USD + * Quote Address: 0xe04676B9A9A2973BCb0D1478b5E1E9098BBB7f3D + * Decimals: 18 + * + * Network: AVAX Testnet + * Base: LINK/USD + * Base Address: 0x34C4c526902d88a3Aa98DB8a9b802603EB1E3470 + * Quote: AVAX/USD + * Quote Address: 0x5498BB86BC934c8D34FDA08E81D444153d0D06aD + * Decimals: 18 + * + * Chainlink Data Feeds can be used in combination to derive denominated price pairs in other + * currencies. + * + * If you require a denomination other than what is provided, you can use two data feeds to derive + * the pair that you need. + * + * For example, if you needed a LINK / FTM price, you could take the LINK / USD feed and the + * FTM / USD feed and derive LINK / FTM using division. + * (LINK/USD)/(FTM/USD) = LINK/FTM + */ +contract DerivedPriceFeed is AggregatorV3Interface { + uint256 public constant override version = 0; + + AggregatorV3Interface public immutable BASE; + AggregatorV3Interface public immutable QUOTE; + uint8 public immutable DECIMALS; + + constructor( + address _base, + address _quote, + uint8 _decimals + ) { + require(_decimals > uint8(0) && _decimals <= uint8(18), "Invalid _decimals"); + DECIMALS = _decimals; + BASE = AggregatorV3Interface(_base); + QUOTE = AggregatorV3Interface(_quote); + } + + function decimals() external view override returns (uint8) { + return DECIMALS; + } + + function getRoundData(uint80) + external + pure + override + returns ( + uint80, + int256, + uint256, + uint256, + uint80 + ) + { + revert("not implemented - use latestRoundData()"); + } + + function description() external pure override returns (string memory) { + return "DerivedPriceFeed.sol"; + } + + function latestRoundData() + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return (uint80(0), getDerivedPrice(), block.timestamp, block.timestamp, uint80(0)); + } + + // https://docs.chain.link/docs/get-the-latest-price/#getting-a-different-price-denomination + function getDerivedPrice() internal view returns (int256) { + (, int256 basePrice, , , ) = BASE.latestRoundData(); + uint8 baseDecimals = BASE.decimals(); + basePrice = scalePrice(basePrice, baseDecimals, DECIMALS); + + (, int256 quotePrice, , , ) = QUOTE.latestRoundData(); + uint8 quoteDecimals = QUOTE.decimals(); + quotePrice = scalePrice(quotePrice, quoteDecimals, DECIMALS); + + return (basePrice * int256(10**uint256(DECIMALS))) / quotePrice; + } + + function scalePrice( + int256 _price, + uint8 _priceDecimals, + uint8 _decimals + ) internal pure returns (int256) { + if (_priceDecimals < _decimals) { + return _price * int256(10**uint256(_decimals - _priceDecimals)); + } else if (_priceDecimals > _decimals) { + return _price / int256(10**uint256(_priceDecimals - _decimals)); + } + return _price; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ExecutionPrevention.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ExecutionPrevention.sol new file mode 100644 index 0000000..a8baf55 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ExecutionPrevention.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +abstract contract ExecutionPrevention { + error OnlySimulatedBackend(); + + /** + * @notice method that allows it to be simulated via eth_call by checking that + * the sender is the zero address. + */ + function preventExecution() internal view { + if (tx.origin != address(0)) { + revert OnlySimulatedBackend(); + } + } + + /** + * @notice modifier that allows it to be simulated via eth_call by checking + * that the sender is the zero address. + */ + modifier cannotExecute() { + preventExecution(); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Flags.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Flags.sol new file mode 100644 index 0000000..11ffa61 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/Flags.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "../SimpleReadAccessController.sol"; +import "../interfaces/AccessControllerInterface.sol"; +import "../interfaces/TypeAndVersionInterface.sol"; + +/* dev dependencies - to be re/moved after audit */ +import "./interfaces/FlagsInterface.sol"; + +/** + * @title The Flags contract + * @notice Allows flags to signal to any reader on the access control list. + * The owner can set flags, or designate other addresses to set flags. + * Raise flag actions are controlled by its own access controller. + * Lower flag actions are controlled by its own access controller. + * An expected pattern is to allow addresses to raise flags on themselves, so if you are subscribing to + * FlagOn events you should filter for addresses you care about. + */ +contract Flags is TypeAndVersionInterface, FlagsInterface, SimpleReadAccessController { + AccessControllerInterface public raisingAccessController; + AccessControllerInterface public loweringAccessController; + + mapping(address => bool) private flags; + + event FlagRaised(address indexed subject); + event FlagLowered(address indexed subject); + event RaisingAccessControllerUpdated(address indexed previous, address indexed current); + event LoweringAccessControllerUpdated(address indexed previous, address indexed current); + + /** + * @param racAddress address for the raising access controller. + * @param lacAddress address for the lowering access controller. + */ + constructor(address racAddress, address lacAddress) { + setRaisingAccessController(racAddress); + setLoweringAccessController(lacAddress); + } + + /** + * @notice versions: + * + * - Flags 1.1.0: upgraded to solc 0.8, added lowering access controller + * - Flags 1.0.0: initial release + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "Flags 1.1.0"; + } + + /** + * @notice read the warning flag status of a contract address. + * @param subject The contract address being checked for a flag. + * @return A true value indicates that a flag was raised and a + * false value indicates that no flag was raised. + */ + function getFlag(address subject) external view override checkAccess returns (bool) { + return flags[subject]; + } + + /** + * @notice read the warning flag status of a contract address. + * @param subjects An array of addresses being checked for a flag. + * @return An array of bools where a true value for any flag indicates that + * a flag was raised and a false value indicates that no flag was raised. + */ + function getFlags(address[] calldata subjects) external view override checkAccess returns (bool[] memory) { + bool[] memory responses = new bool[](subjects.length); + for (uint256 i = 0; i < subjects.length; i++) { + responses[i] = flags[subjects[i]]; + } + return responses; + } + + /** + * @notice enable the warning flag for an address. + * Access is controlled by raisingAccessController, except for owner + * who always has access. + * @param subject The contract address whose flag is being raised + */ + function raiseFlag(address subject) external override { + require(_allowedToRaiseFlags(), "Not allowed to raise flags"); + + _tryToRaiseFlag(subject); + } + + /** + * @notice enable the warning flags for multiple addresses. + * Access is controlled by raisingAccessController, except for owner + * who always has access. + * @param subjects List of the contract addresses whose flag is being raised + */ + function raiseFlags(address[] calldata subjects) external override { + require(_allowedToRaiseFlags(), "Not allowed to raise flags"); + + for (uint256 i = 0; i < subjects.length; i++) { + _tryToRaiseFlag(subjects[i]); + } + } + + /** + * @notice allows owner to disable the warning flags for an addresses. + * Access is controlled by loweringAccessController, except for owner + * who always has access. + * @param subject The contract address whose flag is being lowered + */ + function lowerFlag(address subject) external override { + require(_allowedToLowerFlags(), "Not allowed to lower flags"); + + _tryToLowerFlag(subject); + } + + /** + * @notice allows owner to disable the warning flags for multiple addresses. + * Access is controlled by loweringAccessController, except for owner + * who always has access. + * @param subjects List of the contract addresses whose flag is being lowered + */ + function lowerFlags(address[] calldata subjects) external override { + require(_allowedToLowerFlags(), "Not allowed to lower flags"); + + for (uint256 i = 0; i < subjects.length; i++) { + address subject = subjects[i]; + + _tryToLowerFlag(subject); + } + } + + /** + * @notice allows owner to change the access controller for raising flags. + * @param racAddress new address for the raising access controller. + */ + function setRaisingAccessController(address racAddress) public override onlyOwner { + address previous = address(raisingAccessController); + + if (previous != racAddress) { + raisingAccessController = AccessControllerInterface(racAddress); + + emit RaisingAccessControllerUpdated(previous, racAddress); + } + } + + function setLoweringAccessController(address lacAddress) public override onlyOwner { + address previous = address(loweringAccessController); + + if (previous != lacAddress) { + loweringAccessController = AccessControllerInterface(lacAddress); + + emit LoweringAccessControllerUpdated(previous, lacAddress); + } + } + + // PRIVATE + function _allowedToRaiseFlags() private view returns (bool) { + return msg.sender == owner() || raisingAccessController.hasAccess(msg.sender, msg.data); + } + + function _allowedToLowerFlags() private view returns (bool) { + return msg.sender == owner() || loweringAccessController.hasAccess(msg.sender, msg.data); + } + + function _tryToRaiseFlag(address subject) private { + if (!flags[subject]) { + flags[subject] = true; + emit FlagRaised(subject); + } + } + + function _tryToLowerFlag(address subject) private { + if (flags[subject]) { + flags[subject] = false; + emit FlagLowered(subject); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistry1_3.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistry1_3.sol new file mode 100644 index 0000000..e24e59b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistry1_3.sol @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/proxy/Proxy.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "./KeeperRegistryBase.sol"; +import "./KeeperRegistryLogic1_3.sol"; +import {KeeperRegistryExecutableInterface} from "./interfaces/KeeperRegistryInterface1_3.sol"; +import "../interfaces/MigratableKeeperRegistryInterface.sol"; +import "../interfaces/TypeAndVersionInterface.sol"; +import "../interfaces/ERC677ReceiverInterface.sol"; + +/** + * @notice Registry for adding work for Chainlink Keepers to perform on client + * contracts. Clients must support the Upkeep interface. + */ +contract KeeperRegistry1_3 is + KeeperRegistryBase, + Proxy, + TypeAndVersionInterface, + KeeperRegistryExecutableInterface, + MigratableKeeperRegistryInterface, + ERC677ReceiverInterface +{ + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + + address public immutable KEEPER_REGISTRY_LOGIC; + + /** + * @notice versions: + * - KeeperRegistry 1.3.0: split contract into Proxy and Logic + * : account for Arbitrum and Optimism L1 gas fee + * : allow users to configure upkeeps + * - KeeperRegistry 1.2.0: allow funding within performUpkeep + * : allow configurable registry maxPerformGas + * : add function to let admin change upkeep gas limit + * : add minUpkeepSpend requirement + : upgrade to solidity v0.8 + * - KeeperRegistry 1.1.0: added flatFeeMicroLink + * - KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistry 1.3.0"; + + /** + * @param keeperRegistryLogic the address of keeper registry logic + * @param config registry config settings + */ + constructor(KeeperRegistryLogic1_3 keeperRegistryLogic, Config memory config) + KeeperRegistryBase( + keeperRegistryLogic.PAYMENT_MODEL(), + keeperRegistryLogic.REGISTRY_GAS_OVERHEAD(), + address(keeperRegistryLogic.LINK()), + address(keeperRegistryLogic.LINK_ETH_FEED()), + address(keeperRegistryLogic.FAST_GAS_FEED()) + ) + { + KEEPER_REGISTRY_LOGIC = address(keeperRegistryLogic); + setConfig(config); + } + + // ACTIONS + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external override returns (uint256 id) { + // Executed through logic contract + _fallback(); + } + + /** + * @notice simulated by keepers via eth_call to see if the upkeep needs to be + * performed. If upkeep is needed, the call then simulates performUpkeep + * to make sure it succeeds. Finally, it returns the success status along with + * payment information and the perform data payload. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function checkUpkeep(uint256 id, address from) + external + override + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + // Executed through logic contract + _fallback(); + } + + /** + * @notice executes the upkeep with the perform data returned from + * checkUpkeep, validates the keeper's permissions, and pays the keeper. + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + */ + function performUpkeep(uint256 id, bytes calldata performData) + external + override + whenNotPaused + returns (bool success) + { + return _performUpkeepWithParams(_generatePerformParams(msg.sender, id, performData, true)); + } + + /** + * @notice prevent an upkeep from being performed in the future + * @param id upkeep to be canceled + */ + function cancelUpkeep(uint256 id) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice pause an upkeep + * @param id upkeep to be paused + */ + function pauseUpkeep(uint256 id) external override { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + if (upkeep.paused) revert OnlyUnpausedUpkeep(); + s_upkeep[id].paused = true; + s_upkeepIDs.remove(id); + emit UpkeepPaused(id); + } + + /** + * @notice unpause an upkeep + * @param id upkeep to be resumed + */ + function unpauseUpkeep(uint256 id) external override { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + if (!upkeep.paused) revert OnlyPausedUpkeep(); + s_upkeep[id].paused = false; + s_upkeepIDs.add(id); + emit UpkeepUnpaused(id); + } + + /** + * @notice update the check data of an upkeep + * @param id the id of the upkeep whose check data needs to be updated + * @param newCheckData the new check data + */ + function updateCheckData(uint256 id, bytes calldata newCheckData) external override { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + s_checkData[id] = newCheckData; + emit UpkeepCheckDataUpdated(id, newCheckData); + } + + /** + * @notice adds LINK funding for an upkeep by transferring from the sender's + * LINK balance + * @param id upkeep to fund + * @param amount number of LINK to transfer + */ + function addFunds(uint256 id, uint96 amount) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external override { + if (msg.sender != address(LINK)) revert OnlyCallableByLINKToken(); + if (data.length != 32) revert InvalidDataLength(); + uint256 id = abi.decode(data, (uint256)); + if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + + s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); + s_expectedLinkBalance = s_expectedLinkBalance + amount; + + emit FundsAdded(id, sender, uint96(amount)); + } + + /** + * @notice removes funding from a canceled upkeep + * @param id upkeep to withdraw funds from + * @param to destination address for sending remaining funds + */ + function withdrawFunds(uint256 id, address to) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice withdraws LINK funds collected through cancellation fees + */ + function withdrawOwnerFunds() external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice allows the admin of an upkeep to modify gas limit + * @param id upkeep to be change the gas limit for + * @param gasLimit new gas limit for the upkeep + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice recovers LINK funds improperly transferred to the registry + * @dev In principle this function’s execution cost could exceed block + * gas limit. However, in our anticipated deployment, the number of upkeeps and + * keepers will be low enough to avoid this problem. + */ + function recoverFunds() external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice withdraws a keeper's payment, callable only by the keeper's payee + * @param from keeper address + * @param to address to send the payment to + */ + function withdrawPayment(address from, address to) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice proposes the safe transfer of a keeper's payee to another address + * @param keeper address of the keeper to transfer payee role + * @param proposed address to nominate for next payeeship + */ + function transferPayeeship(address keeper, address proposed) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice accepts the safe transfer of payee role for a keeper + * @param keeper address to accept the payee role for + */ + function acceptPayeeship(address keeper) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice proposes the safe transfer of an upkeep's admin role to another address + * @param id the upkeep id to transfer admin + * @param proposed address to nominate for the new upkeep admin + */ + function transferUpkeepAdmin(uint256 id, address proposed) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice accepts the safe transfer of admin role for an upkeep + * @param id the upkeep id + */ + function acceptUpkeepAdmin(uint256 id) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice signals to keepers that they should not perform upkeeps until the + * contract has been unpaused + */ + function pause() external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice signals to keepers that they can perform upkeeps once again after + * having been paused + */ + function unpause() external { + // Executed through logic contract + _fallback(); + } + + // SETTERS + + /** + * @notice updates the configuration of the registry + * @param config registry config fields + */ + function setConfig(Config memory config) public onlyOwner { + if (config.maxPerformGas < s_storage.maxPerformGas) revert GasLimitCanOnlyIncrease(); + s_storage = Storage({ + paymentPremiumPPB: config.paymentPremiumPPB, + flatFeeMicroLink: config.flatFeeMicroLink, + blockCountPerTurn: config.blockCountPerTurn, + checkGasLimit: config.checkGasLimit, + stalenessSeconds: config.stalenessSeconds, + gasCeilingMultiplier: config.gasCeilingMultiplier, + minUpkeepSpend: config.minUpkeepSpend, + maxPerformGas: config.maxPerformGas, + nonce: s_storage.nonce + }); + s_fallbackGasPrice = config.fallbackGasPrice; + s_fallbackLinkPrice = config.fallbackLinkPrice; + s_transcoder = config.transcoder; + s_registrar = config.registrar; + emit ConfigSet(config); + } + + /** + * @notice update the list of keepers allowed to perform upkeep + * @param keepers list of addresses allowed to perform upkeep + * @param payees addresses corresponding to keepers who are allowed to + * move payments which have been accrued + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external { + // Executed through logic contract + _fallback(); + } + + // GETTERS + + /** + * @notice read all of the details about an upkeep + */ + function getUpkeep(uint256 id) + external + view + override + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent, + bool paused + ) + { + Upkeep memory reg = s_upkeep[id]; + return ( + reg.target, + reg.executeGas, + s_checkData[id], + reg.balance, + reg.lastKeeper, + reg.admin, + reg.maxValidBlocknumber, + reg.amountSpent, + reg.paused + ); + } + + /** + * @notice retrieve active upkeep IDs. Active upkeep is defined as an upkeep which is not paused and not canceled. + * @param startIndex starting index in list + * @param maxCount max count to retrieve (0 = unlimited) + * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one + * should consider keeping the blockheight constant to ensure a holistic picture of the contract state + */ + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view override returns (uint256[] memory) { + uint256 maxIdx = s_upkeepIDs.length(); + if (startIndex >= maxIdx) revert IndexOutOfRange(); + if (maxCount == 0) { + maxCount = maxIdx - startIndex; + } + uint256[] memory ids = new uint256[](maxCount); + for (uint256 idx = 0; idx < maxCount; idx++) { + ids[idx] = s_upkeepIDs.at(startIndex + idx); + } + return ids; + } + + /** + * @notice read the current info about any keeper address + */ + function getKeeperInfo(address query) + external + view + override + returns ( + address payee, + bool active, + uint96 balance + ) + { + KeeperInfo memory keeper = s_keeperInfo[query]; + return (keeper.payee, keeper.active, keeper.balance); + } + + /** + * @notice read the current state of the registry + */ + function getState() + external + view + override + returns ( + State memory state, + Config memory config, + address[] memory keepers + ) + { + Storage memory store = s_storage; + state.nonce = store.nonce; + state.ownerLinkBalance = s_ownerLinkBalance; + state.expectedLinkBalance = s_expectedLinkBalance; + state.numUpkeeps = s_upkeepIDs.length(); + config.paymentPremiumPPB = store.paymentPremiumPPB; + config.flatFeeMicroLink = store.flatFeeMicroLink; + config.blockCountPerTurn = store.blockCountPerTurn; + config.checkGasLimit = store.checkGasLimit; + config.stalenessSeconds = store.stalenessSeconds; + config.gasCeilingMultiplier = store.gasCeilingMultiplier; + config.minUpkeepSpend = store.minUpkeepSpend; + config.maxPerformGas = store.maxPerformGas; + config.fallbackGasPrice = s_fallbackGasPrice; + config.fallbackLinkPrice = s_fallbackLinkPrice; + config.transcoder = s_transcoder; + config.registrar = s_registrar; + return (state, config, s_keeperList); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { + return getMaxPaymentForGas(s_upkeep[id].executeGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + * @param gasLimit the gas to calculate payment for + */ + function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { + (uint256 fastGasWei, uint256 linkEth) = _getFeedData(); + return _calculatePaymentAmount(gasLimit, fastGasWei, linkEth, false); + } + + /** + * @notice retrieves the migration permission for a peer registry + */ + function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { + return s_peerRegistryMigrationPermission[peer]; + } + + /** + * @notice sets the peer registry migration permission + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external { + // Executed through logic contract + _fallback(); + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + UpkeepFormat public constant override upkeepTranscoderVersion = UPKEEP_TRANSCODER_VERSION_BASE; + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @dev This is the address to which proxy functions are delegated to + */ + function _implementation() internal view override returns (address) { + return KEEPER_REGISTRY_LOGIC; + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available + */ + function _callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= PERFORM_GAS_CUSHION and check for underflow + if lt(g, PERFORM_GAS_CUSHION) { + revert(0, 0) + } + g := sub(g, PERFORM_GAS_CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * keeper and the exact gas required by the Upkeep + */ + function _performUpkeepWithParams(PerformParams memory params) private nonReentrant returns (bool success) { + Upkeep memory upkeep = s_upkeep[params.id]; + if (upkeep.maxValidBlocknumber <= block.number) revert UpkeepCancelled(); + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + uint256 gasUsed = gasleft(); + bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); + success = _callWithExactGas(params.gasLimit, upkeep.target, callData); + gasUsed = gasUsed - gasleft(); + uint96 payment = _calculatePaymentAmount(gasUsed, params.fastGasWei, params.linkEth, true); + + s_upkeep[params.id].balance = s_upkeep[params.id].balance - payment; + s_upkeep[params.id].amountSpent = s_upkeep[params.id].amountSpent + payment; + s_upkeep[params.id].lastKeeper = params.from; + s_keeperInfo[params.from].balance = s_keeperInfo[params.from].balance + payment; + + emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); + return success; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryBase.sol new file mode 100644 index 0000000..1ff2811 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryBase.sol @@ -0,0 +1,308 @@ +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/security/Pausable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import "./vendor/@eth-optimism/contracts/0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; +import "./ExecutionPrevention.sol"; +import {Config, State, Upkeep} from "./interfaces/KeeperRegistryInterface1_3.sol"; +import "../ConfirmedOwner.sol"; +import "../interfaces/AggregatorV3Interface.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; +import "../interfaces/UpkeepTranscoderInterface.sol"; + +/** + * @notice Base Keeper Registry contract, contains shared logic between + * KeeperRegistry and KeeperRegistryLogic + */ +abstract contract KeeperRegistryBase is ConfirmedOwner, ExecutionPrevention, ReentrancyGuard, Pausable { + address internal constant ZERO_ADDRESS = address(0); + address internal constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 internal constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 internal constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + uint256 internal constant PERFORM_GAS_MIN = 2_300; + uint256 internal constant CANCELLATION_DELAY = 50; + uint256 internal constant PERFORM_GAS_CUSHION = 5_000; + uint256 internal constant PPB_BASE = 1_000_000_000; + uint32 internal constant UINT32_MAX = type(uint32).max; + uint96 internal constant LINK_TOTAL_SUPPLY = 1e27; + UpkeepFormat internal constant UPKEEP_TRANSCODER_VERSION_BASE = UpkeepFormat.V2; + // L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism + bytes internal constant L1_FEE_DATA_PADDING = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + // MAX_INPUT_DATA represents the estimated max size of the sum of L1 data padding and msg.data in performUpkeep + // function, which includes 4 bytes for function selector, 32 bytes for upkeep id, 35 bytes for data padding, and + // 64 bytes for estimated perform data + bytes internal constant MAX_INPUT_DATA = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + + address[] internal s_keeperList; + EnumerableSet.UintSet internal s_upkeepIDs; + mapping(uint256 => Upkeep) internal s_upkeep; + mapping(address => KeeperInfo) internal s_keeperInfo; + mapping(address => address) internal s_proposedPayee; + mapping(uint256 => address) internal s_proposedAdmin; + mapping(uint256 => bytes) internal s_checkData; + mapping(address => MigrationPermission) internal s_peerRegistryMigrationPermission; + Storage internal s_storage; + uint256 internal s_fallbackGasPrice; // not in config object for gas savings + uint256 internal s_fallbackLinkPrice; // not in config object for gas savings + uint96 internal s_ownerLinkBalance; + uint256 internal s_expectedLinkBalance; + address internal s_transcoder; + address internal s_registrar; + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + AggregatorV3Interface public immutable FAST_GAS_FEED; + OVM_GasPriceOracle public immutable OPTIMISM_ORACLE = OVM_GasPriceOracle(0x420000000000000000000000000000000000000F); + ArbGasInfo public immutable ARB_NITRO_ORACLE = ArbGasInfo(0x000000000000000000000000000000000000006C); + PaymentModel public immutable PAYMENT_MODEL; + uint256 public immutable REGISTRY_GAS_OVERHEAD; + + error ArrayHasNoEntries(); + error CannotCancel(); + error DuplicateEntry(); + error EmptyAddress(); + error GasLimitCanOnlyIncrease(); + error GasLimitOutsideRange(); + error IndexOutOfRange(); + error InsufficientFunds(); + error InvalidDataLength(); + error InvalidPayee(); + error InvalidRecipient(); + error KeepersMustTakeTurns(); + error MigrationNotPermitted(); + error NotAContract(); + error OnlyActiveKeepers(); + error OnlyCallableByAdmin(); + error OnlyCallableByLINKToken(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedAdmin(); + error OnlyCallableByProposedPayee(); + error OnlyPausedUpkeep(); + error OnlyUnpausedUpkeep(); + error ParameterLengthError(); + error PaymentGreaterThanAllLINK(); + error TargetCheckReverted(bytes reason); + error TranscoderNotSet(); + error UpkeepCancelled(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error ValueNotChanged(); + + enum MigrationPermission { + NONE, + OUTGOING, + INCOMING, + BIDIRECTIONAL + } + + enum PaymentModel { + DEFAULT, + ARBITRUM, + OPTIMISM + } + + /** + * @notice storage of the registry, contains a mix of config and state data + */ + struct Storage { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; // 1 full evm word + uint32 maxPerformGas; + uint32 nonce; + } + + struct KeeperInfo { + address payee; + uint96 balance; + bool active; + } + + struct PerformParams { + address from; + uint256 id; + bytes performData; + uint256 maxLinkPayment; + uint256 gasLimit; + uint256 fastGasWei; + uint256 linkEth; + } + + event ConfigSet(Config config); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event KeepersUpdated(address[] keepers, address[] payees); + event OwnerFundsWithdrawn(uint96 amount); + event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); + event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event UpkeepCheckDataUpdated(uint256 indexed id, bytes newCheckData); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepPaused(uint256 indexed id); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + address indexed from, + uint96 payment, + bytes performData + ); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event UpkeepUnpaused(uint256 indexed id); + event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); + + /** + * @param paymentModel the payment model of default, Arbitrum, or Optimism + * @param registryGasOverhead the gas overhead used by registry in performUpkeep + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + */ + constructor( + PaymentModel paymentModel, + uint256 registryGasOverhead, + address link, + address linkEthFeed, + address fastGasFeed + ) ConfirmedOwner(msg.sender) { + PAYMENT_MODEL = paymentModel; + REGISTRY_GAS_OVERHEAD = registryGasOverhead; + if (ZERO_ADDRESS == link || ZERO_ADDRESS == linkEthFeed || ZERO_ADDRESS == fastGasFeed) { + revert EmptyAddress(); + } + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); + } + + /** + * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function _getFeedData() internal view returns (uint256 gasWei, uint256 linkEth) { + uint32 stalenessSeconds = s_storage.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + linkEth = s_fallbackLinkPrice; + } else { + linkEth = uint256(feedValue); + } + return (gasWei, linkEth); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + * @param gasLimit the amount of gas used + * @param fastGasWei the fast gas price + * @param linkEth the exchange ratio between LINK and ETH + * @param isExecution if this is triggered by a perform upkeep function + */ + function _calculatePaymentAmount( + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkEth, + bool isExecution + ) internal view returns (uint96 payment) { + Storage memory store = s_storage; + uint256 gasWei = fastGasWei * store.gasCeilingMultiplier; + // in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier + if (isExecution && tx.gasprice < gasWei) { + gasWei = tx.gasprice; + } + + uint256 weiForGas = gasWei * (gasLimit + REGISTRY_GAS_OVERHEAD); + uint256 premium = PPB_BASE + store.paymentPremiumPPB; + uint256 l1CostWei = 0; + if (PAYMENT_MODEL == PaymentModel.OPTIMISM) { + bytes memory txCallData = new bytes(0); + if (isExecution) { + txCallData = bytes.concat(msg.data, L1_FEE_DATA_PADDING); + } else { + txCallData = MAX_INPUT_DATA; + } + l1CostWei = OPTIMISM_ORACLE.getL1Fee(txCallData); + } else if (PAYMENT_MODEL == PaymentModel.ARBITRUM) { + l1CostWei = ARB_NITRO_ORACLE.getCurrentTxL1GasFees(); + } + // if it's not performing upkeeps, use gas ceiling multiplier to estimate the upper bound + if (!isExecution) { + l1CostWei = store.gasCeilingMultiplier * l1CostWei; + } + + uint256 total = ((weiForGas + l1CostWei) * 1e9 * premium) / linkEth + uint256(store.flatFeeMicroLink) * 1e12; + if (total > LINK_TOTAL_SUPPLY) revert PaymentGreaterThanAllLINK(); + return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX + } + + /** + * @dev ensures all required checks are passed before an upkeep is performed + */ + function _prePerformUpkeep( + Upkeep memory upkeep, + address from, + uint256 maxLinkPayment + ) internal view { + if (upkeep.paused) revert OnlyUnpausedUpkeep(); + if (!s_keeperInfo[from].active) revert OnlyActiveKeepers(); + if (upkeep.balance < maxLinkPayment) revert InsufficientFunds(); + if (upkeep.lastKeeper == from) revert KeepersMustTakeTurns(); + } + + /** + * @dev ensures the upkeep is not cancelled and the caller is the upkeep admin + */ + function requireAdminAndNotCancelled(Upkeep memory upkeep) internal view { + if (msg.sender != upkeep.admin) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + } + + /** + * @dev generates a PerformParams struct for use in _performUpkeepWithParams() + */ + function _generatePerformParams( + address from, + uint256 id, + bytes memory performData, + bool isExecution + ) internal view returns (PerformParams memory) { + uint256 gasLimit = s_upkeep[id].executeGas; + (uint256 fastGasWei, uint256 linkEth) = _getFeedData(); + uint96 maxLinkPayment = _calculatePaymentAmount(gasLimit, fastGasWei, linkEth, isExecution); + + return + PerformParams({ + from: from, + id: id, + performData: performData, + maxLinkPayment: maxLinkPayment, + gasLimit: gasLimit, + fastGasWei: fastGasWei, + linkEth: linkEth + }); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryCheckUpkeepGasUsageWrapper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryCheckUpkeepGasUsageWrapper.sol new file mode 100644 index 0000000..8199364 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryCheckUpkeepGasUsageWrapper.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import {KeeperRegistryExecutableInterface} from "../interfaces/KeeperRegistryInterface.sol"; +import {ConfirmedOwner} from "../ConfirmedOwner.sol"; + +/** + * @notice This contract serves as a wrapper around a keeper registry's checkUpkeep function. + */ +contract KeeperRegistryCheckUpkeepGasUsageWrapper is ConfirmedOwner { + KeeperRegistryExecutableInterface private immutable i_keeperRegistry; + + /** + * @param keeperRegistry address of a keeper registry + */ + constructor(KeeperRegistryExecutableInterface keeperRegistry) ConfirmedOwner(msg.sender) { + i_keeperRegistry = keeperRegistry; + } + + /** + * @return the keeper registry + */ + function getKeeperRegistry() external view returns (KeeperRegistryExecutableInterface) { + return i_keeperRegistry; + } + + /** + * @notice This function is called by monitoring service to estimate how much gas checkUpkeep functions will consume. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function measureCheckGas(uint256 id, address from) + external + returns ( + bool, + bytes memory, + uint256 + ) + { + uint256 startGas = gasleft(); + try i_keeperRegistry.checkUpkeep(id, from) returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) { + uint256 gasUsed = startGas - gasleft(); + return (true, performData, gasUsed); + } catch { + uint256 gasUsed = startGas - gasleft(); + return (false, "", gasUsed); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.sol new file mode 100644 index 0000000..96c4c11 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import {AutomationRegistryExecutableInterface} from "../interfaces/AutomationRegistryInterface1_2.sol"; +import {ConfirmedOwner} from "../ConfirmedOwner.sol"; + +/** + * @notice This contract serves as a wrapper around a keeper registry's checkUpkeep function. + */ +contract KeeperRegistryCheckUpkeepGasUsageWrapper1_2 is ConfirmedOwner { + AutomationRegistryExecutableInterface private immutable i_keeperRegistry; + + /** + * @param keeperRegistry address of a keeper registry + */ + constructor(AutomationRegistryExecutableInterface keeperRegistry) ConfirmedOwner(msg.sender) { + i_keeperRegistry = keeperRegistry; + } + + /** + * @return the keeper registry + */ + function getKeeperRegistry() external view returns (AutomationRegistryExecutableInterface) { + return i_keeperRegistry; + } + + /** + * @notice This function is called by monitoring service to estimate how much gas checkUpkeep functions will consume. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function measureCheckGas(uint256 id, address from) + external + returns ( + bool, + bytes memory, + uint256 + ) + { + uint256 startGas = gasleft(); + try i_keeperRegistry.checkUpkeep(id, from) returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) { + uint256 gasUsed = startGas - gasleft(); + return (true, performData, gasUsed); + } catch { + uint256 gasUsed = startGas - gasleft(); + return (false, "", gasUsed); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryLogic1_3.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryLogic1_3.sol new file mode 100644 index 0000000..c07d613 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/KeeperRegistryLogic1_3.sol @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "./KeeperRegistryBase.sol"; +import "../interfaces/MigratableKeeperRegistryInterface.sol"; +import "../interfaces/UpkeepTranscoderInterface.sol"; + +/** + * @notice Logic contract, works in tandem with KeeperRegistry as a proxy + */ +contract KeeperRegistryLogic1_3 is KeeperRegistryBase { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + + /** + * @param paymentModel one of Default, Arbitrum, Optimism + * @param registryGasOverhead the gas overhead used by registry in performUpkeep + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + */ + constructor( + PaymentModel paymentModel, + uint256 registryGasOverhead, + address link, + address linkEthFeed, + address fastGasFeed + ) KeeperRegistryBase(paymentModel, registryGasOverhead, link, linkEthFeed, fastGasFeed) {} + + function checkUpkeep(uint256 id, address from) + external + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + Upkeep memory upkeep = s_upkeep[id]; + + bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); + (bool success, bytes memory result) = upkeep.target.call{gas: s_storage.checkGasLimit}(callData); + + if (!success) revert TargetCheckReverted(result); + + (success, performData) = abi.decode(result, (bool, bytes)); + if (!success) revert UpkeepNotNeeded(); + + PerformParams memory params = _generatePerformParams(from, id, performData, false); + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + return ( + performData, + params.maxLinkPayment, + params.gasLimit, + // adjustedGasWei equals fastGasWei multiplies gasCeilingMultiplier in non-execution cases + params.fastGasWei * s_storage.gasCeilingMultiplier, + params.linkEth + ); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function withdrawOwnerFunds() external onlyOwner { + uint96 amount = s_ownerLinkBalance; + + s_expectedLinkBalance = s_expectedLinkBalance - amount; + s_ownerLinkBalance = 0; + + emit OwnerFundsWithdrawn(amount); + LINK.transfer(msg.sender, amount); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function recoverFunds() external onlyOwner { + uint256 total = LINK.balanceOf(address(this)); + LINK.transfer(msg.sender, total - s_expectedLinkBalance); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { + if (keepers.length != payees.length || keepers.length < 2) revert ParameterLengthError(); + for (uint256 i = 0; i < s_keeperList.length; i++) { + address keeper = s_keeperList[i]; + s_keeperInfo[keeper].active = false; + } + for (uint256 i = 0; i < keepers.length; i++) { + address keeper = keepers[i]; + KeeperInfo storage s_keeper = s_keeperInfo[keeper]; + address oldPayee = s_keeper.payee; + address newPayee = payees[i]; + if ( + (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) + ) revert InvalidPayee(); + if (s_keeper.active) revert DuplicateEntry(); + s_keeper.active = true; + if (newPayee != IGNORE_ADDRESS) { + s_keeper.payee = newPayee; + } + } + s_keeperList = keepers; + emit KeepersUpdated(keepers, payees); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function unpause() external onlyOwner { + _unpause(); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { + s_peerRegistryMigrationPermission[peer] = permission; + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id) { + if (msg.sender != owner() && msg.sender != s_registrar) revert OnlyCallableByOwnerOrRegistrar(); + + id = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), s_storage.nonce))); + _createUpkeep(id, target, gasLimit, admin, 0, checkData, false); + s_storage.nonce++; + emit UpkeepRegistered(id, gasLimit, admin); + return id; + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function cancelUpkeep(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + bool canceled = upkeep.maxValidBlocknumber != UINT32_MAX; + bool isOwner = msg.sender == owner(); + + if (canceled && !(isOwner && upkeep.maxValidBlocknumber > block.number)) revert CannotCancel(); + if (!isOwner && msg.sender != upkeep.admin) revert OnlyCallableByOwnerOrAdmin(); + + uint256 height = block.number; + if (!isOwner) { + height = height + CANCELLATION_DELAY; + } + s_upkeep[id].maxValidBlocknumber = uint32(height); + s_upkeepIDs.remove(id); + + // charge the cancellation fee if the minUpkeepSpend is not met + uint96 minUpkeepSpend = s_storage.minUpkeepSpend; + uint96 cancellationFee = 0; + // cancellationFee is supposed to be min(max(minUpkeepSpend - amountSpent,0), amountLeft) + if (upkeep.amountSpent < minUpkeepSpend) { + cancellationFee = minUpkeepSpend - upkeep.amountSpent; + if (cancellationFee > upkeep.balance) { + cancellationFee = upkeep.balance; + } + } + s_upkeep[id].balance = upkeep.balance - cancellationFee; + s_ownerLinkBalance = s_ownerLinkBalance + cancellationFee; + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function addFunds(uint256 id, uint96 amount) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + + s_upkeep[id].balance = upkeep.balance + amount; + s_expectedLinkBalance = s_expectedLinkBalance + amount; + LINK.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function withdrawFunds(uint256 id, address to) external { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber > block.number) revert UpkeepNotCanceled(); + + uint96 amountToWithdraw = s_upkeep[id].balance; + s_expectedLinkBalance = s_expectedLinkBalance - amountToWithdraw; + s_upkeep[id].balance = 0; + emit FundsWithdrawn(id, amountToWithdraw, to); + + LINK.transfer(to, amountToWithdraw); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external { + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); + + s_upkeep[id].executeGas = gasLimit; + + emit UpkeepGasLimitSet(id, gasLimit); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function withdrawPayment(address from, address to) external { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + KeeperInfo memory keeper = s_keeperInfo[from]; + if (keeper.payee != msg.sender) revert OnlyCallableByPayee(); + + s_keeperInfo[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance - keeper.balance; + emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); + + LINK.transfer(to, keeper.balance); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function transferPayeeship(address keeper, address proposed) external { + if (s_keeperInfo[keeper].payee != msg.sender) revert OnlyCallableByPayee(); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedPayee[keeper] != proposed) { + s_proposedPayee[keeper] = proposed; + emit PayeeshipTransferRequested(keeper, msg.sender, proposed); + } + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function acceptPayeeship(address keeper) external { + if (s_proposedPayee[keeper] != msg.sender) revert OnlyCallableByProposedPayee(); + address past = s_keeperInfo[keeper].payee; + s_keeperInfo[keeper].payee = msg.sender; + s_proposedPayee[keeper] = ZERO_ADDRESS; + + emit PayeeshipTransferred(keeper, past, msg.sender); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function transferUpkeepAdmin(uint256 id, address proposed) external { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + if (proposed == msg.sender) revert ValueNotChanged(); + if (proposed == ZERO_ADDRESS) revert InvalidRecipient(); + + if (s_proposedAdmin[id] != proposed) { + s_proposedAdmin[id] = proposed; + emit UpkeepAdminTransferRequested(id, msg.sender, proposed); + } + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function acceptUpkeepAdmin(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (s_proposedAdmin[id] != msg.sender) revert OnlyCallableByProposedAdmin(); + address past = upkeep.admin; + s_upkeep[id].admin = msg.sender; + s_proposedAdmin[id] = ZERO_ADDRESS; + + emit UpkeepAdminTransferred(id, past, msg.sender); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external { + if ( + s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && + s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + if (s_transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); + if (ids.length == 0) revert ArrayHasNoEntries(); + uint256 id; + Upkeep memory upkeep; + uint256 totalBalanceRemaining; + bytes[] memory checkDatas = new bytes[](ids.length); + Upkeep[] memory upkeeps = new Upkeep[](ids.length); + for (uint256 idx = 0; idx < ids.length; idx++) { + id = ids[idx]; + upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + upkeeps[idx] = upkeep; + checkDatas[idx] = s_checkData[id]; + totalBalanceRemaining = totalBalanceRemaining + upkeep.balance; + delete s_upkeep[id]; + delete s_checkData[id]; + // nullify existing proposed admin change if an upkeep is being migrated + delete s_proposedAdmin[id]; + s_upkeepIDs.remove(id); + emit UpkeepMigrated(id, upkeep.balance, destination); + } + s_expectedLinkBalance = s_expectedLinkBalance - totalBalanceRemaining; + bytes memory encodedUpkeeps = abi.encode(ids, upkeeps, checkDatas); + MigratableKeeperRegistryInterface(destination).receiveUpkeeps( + UpkeepTranscoderInterface(s_transcoder).transcodeUpkeeps( + UPKEEP_TRANSCODER_VERSION_BASE, + MigratableKeeperRegistryInterface(destination).upkeepTranscoderVersion(), + encodedUpkeeps + ) + ); + LINK.transfer(destination, totalBalanceRemaining); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external { + if ( + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + (uint256[] memory ids, Upkeep[] memory upkeeps, bytes[] memory checkDatas) = abi.decode( + encodedUpkeeps, + (uint256[], Upkeep[], bytes[]) + ); + for (uint256 idx = 0; idx < ids.length; idx++) { + _createUpkeep( + ids[idx], + upkeeps[idx].target, + upkeeps[idx].executeGas, + upkeeps[idx].admin, + upkeeps[idx].balance, + checkDatas[idx], + upkeeps[idx].paused + ); + emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); + } + } + + /** + * @notice creates a new upkeep with the given fields + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + * @param paused if this upkeep is paused + */ + function _createUpkeep( + uint256 id, + address target, + uint32 gasLimit, + address admin, + uint96 balance, + bytes memory checkData, + bool paused + ) internal whenNotPaused { + if (!target.isContract()) revert NotAContract(); + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + s_upkeep[id] = Upkeep({ + target: target, + executeGas: gasLimit, + balance: balance, + admin: admin, + maxValidBlocknumber: UINT32_MAX, + lastKeeper: ZERO_ADDRESS, + amountSpent: 0, + paused: paused + }); + s_expectedLinkBalance = s_expectedLinkBalance + balance; + s_checkData[id] = checkData; + s_upkeepIDs.add(id); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismCrossDomainForwarder.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismCrossDomainForwarder.sol new file mode 100644 index 0000000..15a6451 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismCrossDomainForwarder.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/TypeAndVersionInterface.sol"; + +/* ./dev dependencies - to be moved from ./dev after audit */ +import "./CrossDomainForwarder.sol"; +import "./vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol"; +import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; + +/** + * @title OptimismCrossDomainForwarder - L1 xDomain account representation + * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. + * @dev Any other L2 contract which uses this contract's address as a privileged position, + * can be considered to be owned by the `l1Owner` + */ +contract OptimismCrossDomainForwarder is TypeAndVersionInterface, CrossDomainForwarder { + // OVM_L2CrossDomainMessenger is a precompile usually deployed to 0x4200000000000000000000000000000000000007 + iOVM_CrossDomainMessenger private immutable OVM_CROSS_DOMAIN_MESSENGER; + + /** + * @notice creates a new Optimism xDomain Forwarder contract + * @param crossDomainMessengerAddr the xDomain bridge messenger (Optimism bridge L2) contract address + * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn + */ + constructor(iOVM_CrossDomainMessenger crossDomainMessengerAddr, address l1OwnerAddr) CrossDomainOwnable(l1OwnerAddr) { + require(address(crossDomainMessengerAddr) != address(0), "Invalid xDomain Messenger address"); + OVM_CROSS_DOMAIN_MESSENGER = crossDomainMessengerAddr; + } + + /** + * @notice versions: + * + * - OptimismCrossDomainForwarder 0.1.0: initial release + * - OptimismCrossDomainForwarder 1.0.0: Use OZ Address, CrossDomainOwnable + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "OptimismCrossDomainForwarder 1.0.0"; + } + + /** + * @dev forwarded only if L2 Messenger calls with `xDomainMessageSender` being the L1 owner address + * @inheritdoc ForwarderInterface + */ + function forward(address target, bytes memory data) external virtual override onlyL1Owner { + Address.functionCall(target, data, "Forwarder call reverted"); + } + + /** + * @notice This is always the address of the OVM_L2CrossDomainMessenger contract + */ + function crossDomainMessenger() public view returns (address) { + return address(OVM_CROSS_DOMAIN_MESSENGER); + } + + /** + * @notice The call MUST come from the L1 owner (via cross-chain message.) Reverts otherwise. + */ + modifier onlyL1Owner() override { + require(msg.sender == crossDomainMessenger(), "Sender is not the L2 messenger"); + require( + iOVM_CrossDomainMessenger(crossDomainMessenger()).xDomainMessageSender() == l1Owner(), + "xDomain sender is not the L1 owner" + ); + _; + } + + /** + * @notice The call MUST come from the proposed L1 owner (via cross-chain message.) Reverts otherwise. + */ + modifier onlyProposedL1Owner() override { + address messenger = crossDomainMessenger(); + require(msg.sender == messenger, "Sender is not the L2 messenger"); + require( + iOVM_CrossDomainMessenger(messenger).xDomainMessageSender() == s_l1PendingOwner, + "Must be proposed L1 owner" + ); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismCrossDomainGovernor.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismCrossDomainGovernor.sol new file mode 100644 index 0000000..1a95d07 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismCrossDomainGovernor.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./interfaces/DelegateForwarderInterface.sol"; +import "./vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol"; +import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; +import "./OptimismCrossDomainForwarder.sol"; + +/** + * @title OptimismCrossDomainGovernor - L1 xDomain account representation (with delegatecall support) for Optimism + * @notice L2 Contract which receives messages from a specific L1 address and transparently forwards them to the destination. + * @dev Any other L2 contract which uses this contract's address as a privileged position, + * can be considered to be simultaneously owned by the `l1Owner` and L2 `owner` + */ +contract OptimismCrossDomainGovernor is DelegateForwarderInterface, OptimismCrossDomainForwarder { + /** + * @notice creates a new Optimism xDomain Forwarder contract + * @param crossDomainMessengerAddr the xDomain bridge messenger (Optimism bridge L2) contract address + * @param l1OwnerAddr the L1 owner address that will be allowed to call the forward fn + * @dev Empty constructor required due to inheriting from abstract contract CrossDomainForwarder + */ + constructor(iOVM_CrossDomainMessenger crossDomainMessengerAddr, address l1OwnerAddr) + OptimismCrossDomainForwarder(crossDomainMessengerAddr, l1OwnerAddr) + {} + + /** + * @notice versions: + * + * - OptimismCrossDomainForwarder 1.0.0: initial release + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "OptimismCrossDomainGovernor 1.0.0"; + } + + /** + * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner + * @inheritdoc ForwarderInterface + */ + function forward(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { + Address.functionCall(target, data, "Governor call reverted"); + } + + /** + * @dev forwarded only if L2 Messenger calls with `msg.sender` being the L1 owner address, or called by the L2 owner + * @inheritdoc DelegateForwarderInterface + */ + function forwardDelegate(address target, bytes memory data) external override onlyLocalOrCrossDomainOwner { + Address.functionDelegateCall(target, data, "Governor delegatecall reverted"); + } + + /** + * @notice The call MUST come from either the L1 owner (via cross-chain message) or the L2 owner. Reverts otherwise. + */ + modifier onlyLocalOrCrossDomainOwner() { + address messenger = crossDomainMessenger(); + // 1. The delegatecall MUST come from either the L1 owner (via cross-chain message) or the L2 owner + require(msg.sender == messenger || msg.sender == owner(), "Sender is not the L2 messenger or owner"); + // 2. The L2 Messenger's caller MUST be the L1 Owner + if (msg.sender == messenger) { + require( + iOVM_CrossDomainMessenger(messenger).xDomainMessageSender() == l1Owner(), + "xDomain sender is not the L1 owner" + ); + } + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismSequencerUptimeFeed.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismSequencerUptimeFeed.sol new file mode 100644 index 0000000..46c3079 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismSequencerUptimeFeed.sol @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {AggregatorInterface} from "../interfaces/AggregatorInterface.sol"; +import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol"; +import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; +import {OptimismSequencerUptimeFeedInterface} from "./interfaces/OptimismSequencerUptimeFeedInterface.sol"; +import {SimpleReadAccessController} from "../SimpleReadAccessController.sol"; +import {ConfirmedOwner} from "../ConfirmedOwner.sol"; +import {IL2CrossDomainMessenger} from "@eth-optimism/contracts/L2/messaging/IL2CrossDomainMessenger.sol"; + +/** + * @title OptimismSequencerUptimeFeed - L2 sequencer uptime status aggregator + * @notice L2 contract that receives status updates from a specific L1 address, + * records a new answer if the status changed + */ +contract OptimismSequencerUptimeFeed is + AggregatorV2V3Interface, + OptimismSequencerUptimeFeedInterface, + TypeAndVersionInterface, + SimpleReadAccessController +{ + /// @dev Round info (for uptime history) + struct Round { + bool status; + uint64 startedAt; + uint64 updatedAt; + } + + /// @dev Packed state struct to save sloads + struct FeedState { + uint80 latestRoundId; + bool latestStatus; + uint64 startedAt; + uint64 updatedAt; + } + + /// @notice Sender is not the L2 messenger + error InvalidSender(); + /// @notice Replacement for AggregatorV3Interface "No data present" + error NoDataPresent(); + + event L1SenderTransferred(address indexed from, address indexed to); + /// @dev Emitted when an `updateStatus` call is ignored due to unchanged status or stale timestamp + event UpdateIgnored(bool latestStatus, uint64 latestTimestamp, bool incomingStatus, uint64 incomingTimestamp); + /// @dev Emitted when a updateStatus is called without the status changing + event RoundUpdated(int256 status, uint64 updatedAt); + + uint8 public constant override decimals = 0; + string public constant override description = "L2 Sequencer Uptime Status Feed"; + uint256 public constant override version = 1; + + /// @dev L1 address + address private s_l1Sender; + /// @dev s_latestRoundId == 0 means this contract is uninitialized. + FeedState private s_feedState = FeedState({latestRoundId: 0, latestStatus: false, startedAt: 0, updatedAt: 0}); + mapping(uint80 => Round) private s_rounds; + + IL2CrossDomainMessenger private immutable s_l2CrossDomainMessenger; + + /** + * @param l1SenderAddress Address of the L1 contract that is permissioned to call this contract + * @param l2CrossDomainMessengerAddr Address of the L2CrossDomainMessenger contract + * @param initialStatus The initial status of the feed + */ + constructor( + address l1SenderAddress, + address l2CrossDomainMessengerAddr, + bool initialStatus + ) { + setL1Sender(l1SenderAddress); + s_l2CrossDomainMessenger = IL2CrossDomainMessenger(l2CrossDomainMessengerAddr); + uint64 timestamp = uint64(block.timestamp); + + // Initialise roundId == 1 as the first round + recordRound(1, initialStatus, timestamp); + } + + /** + * @notice Check if a roundId is valid in this current contract state + * @dev Mainly used for AggregatorV2V3Interface functions + * @param roundId Round ID to check + */ + function isValidRound(uint256 roundId) private view returns (bool) { + return roundId > 0 && roundId <= type(uint80).max && s_feedState.latestRoundId >= roundId; + } + + /** + * @notice versions: + * + * - OptimismSequencerUptimeFeed 1.0.0: initial release + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "OptimismSequencerUptimeFeed 1.0.0"; + } + + /// @return L1 sender address + function l1Sender() public view virtual returns (address) { + return s_l1Sender; + } + + /** + * @notice Set the allowed L1 sender for this contract to a new L1 sender + * @dev Can be disabled by setting the L1 sender as `address(0)`. Accessible only by owner. + * @param to new L1 sender that will be allowed to call `updateStatus` on this contract + */ + function transferL1Sender(address to) external virtual onlyOwner { + setL1Sender(to); + } + + /// @notice internal method that stores the L1 sender + function setL1Sender(address to) private { + address from = s_l1Sender; + if (from != to) { + s_l1Sender = to; + emit L1SenderTransferred(from, to); + } + } + + /** + * @dev Returns an AggregatorV2V3Interface compatible answer from status flag + * + * @param status The status flag to convert to an aggregator-compatible answer + */ + function getStatusAnswer(bool status) private pure returns (int256) { + return status ? int256(1) : int256(0); + } + + /** + * @notice Helper function to record a round and set the latest feed state. + * + * @param roundId The round ID to record + * @param status Sequencer status + * @param timestamp The L1 block timestamp of status update + */ + function recordRound( + uint80 roundId, + bool status, + uint64 timestamp + ) private { + uint64 updatedAt = uint64(block.timestamp); + Round memory nextRound = Round(status, timestamp, updatedAt); + FeedState memory feedState = FeedState(roundId, status, timestamp, updatedAt); + + s_rounds[roundId] = nextRound; + s_feedState = feedState; + + emit NewRound(roundId, msg.sender, timestamp); + emit AnswerUpdated(getStatusAnswer(status), roundId, timestamp); + } + + /** + * @notice Helper function to update when a round was last updated + * + * @param roundId The round ID to update + * @param status Sequencer status + */ + function updateRound(uint80 roundId, bool status) private { + uint64 updatedAt = uint64(block.timestamp); + s_rounds[roundId].updatedAt = updatedAt; + s_feedState.updatedAt = updatedAt; + emit RoundUpdated(getStatusAnswer(status), updatedAt); + } + + /** + * @notice Record a new status and timestamp if it has changed since the last round. + * @dev This function will revert if not called from `l1Sender` via the L1->L2 messenger. + * + * @param status Sequencer status + * @param timestamp Block timestamp of status update + */ + function updateStatus(bool status, uint64 timestamp) external override { + FeedState memory feedState = s_feedState; + if ( + msg.sender != address(s_l2CrossDomainMessenger) || s_l2CrossDomainMessenger.xDomainMessageSender() != s_l1Sender + ) { + revert InvalidSender(); + } + + // Ignore if latest recorded timestamp is newer + if (feedState.startedAt > timestamp) { + emit UpdateIgnored(feedState.latestStatus, feedState.startedAt, status, timestamp); + return; + } + + if (feedState.latestStatus == status) { + updateRound(feedState.latestRoundId, status); + } else { + feedState.latestRoundId += 1; + recordRound(feedState.latestRoundId, status, timestamp); + } + } + + /// @inheritdoc AggregatorInterface + function latestAnswer() external view override checkAccess returns (int256) { + FeedState memory feedState = s_feedState; + return getStatusAnswer(feedState.latestStatus); + } + + /// @inheritdoc AggregatorInterface + function latestTimestamp() external view override checkAccess returns (uint256) { + FeedState memory feedState = s_feedState; + return feedState.startedAt; + } + + /// @inheritdoc AggregatorInterface + function latestRound() external view override checkAccess returns (uint256) { + FeedState memory feedState = s_feedState; + return feedState.latestRoundId; + } + + /// @inheritdoc AggregatorInterface + function getAnswer(uint256 roundId) external view override checkAccess returns (int256) { + if (isValidRound(roundId)) { + return getStatusAnswer(s_rounds[uint80(roundId)].status); + } + + revert NoDataPresent(); + } + + /// @inheritdoc AggregatorInterface + function getTimestamp(uint256 roundId) external view override checkAccess returns (uint256) { + if (isValidRound(roundId)) { + return s_rounds[uint80(roundId)].startedAt; + } + + revert NoDataPresent(); + } + + /// @inheritdoc AggregatorV3Interface + function getRoundData(uint80 _roundId) + public + view + override + checkAccess + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + if (isValidRound(_roundId)) { + Round memory round = s_rounds[_roundId]; + answer = getStatusAnswer(round.status); + startedAt = uint256(round.startedAt); + roundId = _roundId; + updatedAt = uint256(round.updatedAt); + answeredInRound = roundId; + } else { + revert NoDataPresent(); + } + } + + /// @inheritdoc AggregatorV3Interface + function latestRoundData() + external + view + override + checkAccess + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + FeedState memory feedState = s_feedState; + + roundId = feedState.latestRoundId; + answer = getStatusAnswer(feedState.latestStatus); + startedAt = feedState.startedAt; + updatedAt = feedState.updatedAt; + answeredInRound = roundId; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismValidator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismValidator.sol new file mode 100644 index 0000000..c865c17 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/OptimismValidator.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/AggregatorValidatorInterface.sol"; +import "../interfaces/TypeAndVersionInterface.sol"; +import "../interfaces/AccessControllerInterface.sol"; +import "../interfaces/AggregatorV3Interface.sol"; +import "../SimpleWriteAccessController.sol"; + +import "./interfaces/OptimismSequencerUptimeFeedInterface.sol"; +import "@eth-optimism/contracts/L1/messaging/IL1CrossDomainMessenger.sol"; +import "./vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; + +/** + * @title OptimismValidator - makes cross chain call to update the Sequencer Uptime Feed on L2 + */ +contract OptimismValidator is TypeAndVersionInterface, AggregatorValidatorInterface, SimpleWriteAccessController { + int256 private constant ANSWER_SEQ_OFFLINE = 1; + uint32 private s_gasLimit; + + address public immutable L1_CROSS_DOMAIN_MESSENGER_ADDRESS; + address public immutable L2_UPTIME_FEED_ADDR; + + /** + * @notice emitted when gas cost to spend on L2 is updated + * @param gasLimit updated gas cost + */ + event GasLimitUpdated(uint32 gasLimit); + + /** + * @param l1CrossDomainMessengerAddress address the L1CrossDomainMessenger contract address + * @param l2UptimeFeedAddr the address of the OptimismSequencerUptimeFeed contract address + * @param gasLimit the gasLimit to use for sending a message from L1 to L2 + */ + constructor( + address l1CrossDomainMessengerAddress, + address l2UptimeFeedAddr, + uint32 gasLimit + ) { + require(l1CrossDomainMessengerAddress != address(0), "Invalid xDomain Messenger address"); + require(l2UptimeFeedAddr != address(0), "Invalid OptimismSequencerUptimeFeed contract address"); + L1_CROSS_DOMAIN_MESSENGER_ADDRESS = l1CrossDomainMessengerAddress; + L2_UPTIME_FEED_ADDR = l2UptimeFeedAddr; + s_gasLimit = gasLimit; + } + + /** + * @notice versions: + * + * - OptimismValidator 0.1.0: initial release + * - OptimismValidator 1.0.0: change target of L2 sequencer status update + * - now calls `updateStatus` on an L2 OptimismSequencerUptimeFeed contract instead of + * directly calling the Flags contract + * + * @inheritdoc TypeAndVersionInterface + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "OptimismValidator 1.0.0"; + } + + /** + * @notice sets the new gas cost to spend when sending cross chain message + * @param gasLimit the updated gas cost + */ + function setGasLimit(uint32 gasLimit) external onlyOwner { + s_gasLimit = gasLimit; + emit GasLimitUpdated(gasLimit); + } + + /** + * @notice fetches the gas cost of sending a cross chain message + */ + function getGasLimit() external view returns (uint32) { + return s_gasLimit; + } + + /** + * @notice validate method sends an xDomain L2 tx to update Uptime Feed contract on L2. + * @dev A message is sent using the L1CrossDomainMessenger. This method is accessed controlled. + * @param previousAnswer previous aggregator answer + * @param currentAnswer new aggregator answer - value of 1 considers the sequencer offline. + */ + function validate( + uint256, /* previousRoundId */ + int256 previousAnswer, + uint256, /* currentRoundId */ + int256 currentAnswer + ) external override checkAccess returns (bool) { + // Encode the OptimismSequencerUptimeFeed call + bytes4 selector = OptimismSequencerUptimeFeedInterface.updateStatus.selector; + bool status = currentAnswer == ANSWER_SEQ_OFFLINE; + uint64 timestamp = uint64(block.timestamp); + // Encode `status` and `timestamp` + bytes memory message = abi.encodeWithSelector(selector, status, timestamp); + // Make the xDomain call + IL1CrossDomainMessenger(L1_CROSS_DOMAIN_MESSENGER_ADDRESS).sendMessage( + L2_UPTIME_FEED_ADDR, // target + message, + s_gasLimit + ); + // return success + return true; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRF.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRF.sol new file mode 100644 index 0000000..7e0bbfb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRF.sol @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** **************************************************************************** + * @notice Verification of verifiable-random-function (VRF) proofs, following + * @notice https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @notice See https://eprint.iacr.org/2017/099.pdf for security proofs. + + * @dev Bibliographic references: + + * @dev Goldberg, et al., "Verifiable Random Functions (VRFs)", Internet Draft + * @dev draft-irtf-cfrg-vrf-05, IETF, Aug 11 2019, + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05 + + * @dev Papadopoulos, et al., "Making NSEC5 Practical for DNSSEC", Cryptology + * @dev ePrint Archive, Report 2017/099, https://eprint.iacr.org/2017/099.pdf + * **************************************************************************** + * @dev USAGE + + * @dev The main entry point is randomValueFromVRFProof. See its docstring. + * **************************************************************************** + * @dev PURPOSE + + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is computationally indistinguishable to her from a uniform + * @dev random sample from the output space. + + * @dev The purpose of this contract is to perform that verification. + * **************************************************************************** + * @dev DESIGN NOTES + + * @dev The VRF algorithm verified here satisfies the full uniqueness, full + * @dev collision resistance, and full pseudo-randomness security properties. + * @dev See "SECURITY PROPERTIES" below, and + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-3 + + * @dev An elliptic curve point is generally represented in the solidity code + * @dev as a uint256[2], corresponding to its affine coordinates in + * @dev GF(FIELD_SIZE). + + * @dev For the sake of efficiency, this implementation deviates from the spec + * @dev in some minor ways: + + * @dev - Keccak hash rather than the SHA256 hash recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev Keccak costs much less gas on the EVM, and provides similar security. + + * @dev - Secp256k1 curve instead of the P-256 or ED25519 curves recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev For curve-point multiplication, it's much cheaper to abuse ECRECOVER + + * @dev - hashToCurve recursively hashes until it finds a curve x-ordinate. On + * @dev the EVM, this is slightly more efficient than the recommendation in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + * @dev step 5, to concatenate with a nonce then hash, and rehash with the + * @dev nonce updated until a valid x-ordinate is found. + + * @dev - hashToCurve does not include a cipher version string or the byte 0x1 + * @dev in the hash message, as recommended in step 5.B of the draft + * @dev standard. They are unnecessary here because no variation in the + * @dev cipher suite is allowed. + + * @dev - Similarly, the hash input in scalarFromCurvePoints does not include a + * @dev commitment to the cipher suite, either, which differs from step 2 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + * @dev . Also, the hash input is the concatenation of the uncompressed + * @dev points, not the compressed points as recommended in step 3. + + * @dev - In the calculation of the challenge value "c", the "u" value (i.e. + * @dev the value computed by Reggie as the nonce times the secp256k1 + * @dev generator point, see steps 5 and 7 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @dev ) is replaced by its ethereum address, i.e. the lower 160 bits of the + * @dev keccak hash of the original u. This is because we only verify the + * @dev calculation of u up to its address, by abusing ECRECOVER. + * **************************************************************************** + * @dev SECURITY PROPERTIES + + * @dev Here are the security properties for this VRF: + + * @dev Full uniqueness: For any seed and valid VRF public key, there is + * @dev exactly one VRF output which can be proved to come from that seed, in + * @dev the sense that the proof will pass verifyVRFProof. + + * @dev Full collision resistance: It's cryptographically infeasible to find + * @dev two seeds with same VRF output from a fixed, valid VRF key + + * @dev Full pseudorandomness: Absent the proofs that the VRF outputs are + * @dev derived from a given seed, the outputs are computationally + * @dev indistinguishable from randomness. + + * @dev https://eprint.iacr.org/2017/099.pdf, Appendix B contains the proofs + * @dev for these properties. + + * @dev For secp256k1, the key validation described in section + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.6 + * @dev is unnecessary, because secp256k1 has cofactor 1, and the + * @dev representation of the public key used here (affine x- and y-ordinates + * @dev of the secp256k1 point on the standard y^2=x^3+7 curve) cannot refer to + * @dev the point at infinity. + * **************************************************************************** + * @dev OTHER SECURITY CONSIDERATIONS + * + * @dev The seed input to the VRF could in principle force an arbitrary amount + * @dev of work in hashToCurve, by requiring extra rounds of hashing and + * @dev checking whether that's yielded the x ordinate of a secp256k1 point. + * @dev However, under the Random Oracle Model the probability of choosing a + * @dev point which forces n extra rounds in hashToCurve is 2⁻ⁿ. The base cost + * @dev for calling hashToCurve is about 25,000 gas, and each round of checking + * @dev for a valid x ordinate costs about 15,555 gas, so to find a seed for + * @dev which hashToCurve would cost more than 2,017,000 gas, one would have to + * @dev try, in expectation, about 2¹²⁸ seeds, which is infeasible for any + * @dev foreseeable computational resources. (25,000 + 128 * 15,555 < 2,017,000.) + + * @dev Since the gas block limit for the Ethereum main net is 10,000,000 gas, + * @dev this means it is infeasible for an adversary to prevent correct + * @dev operation of this contract by choosing an adverse seed. + + * @dev (See TestMeasureHashToCurveGasCost for verification of the gas cost for + * @dev hashToCurve.) + + * @dev It may be possible to make a secure constant-time hashToCurve function. + * @dev See notes in hashToCurve docstring. +*/ +contract VRF { + // See https://www.secg.org/sec2-v2.pdf, section 2.4.1, for these constants. + // Number of points in Secp256k1 + uint256 private constant GROUP_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + // Prime characteristic of the galois field over which Secp256k1 is defined + uint256 private constant FIELD_SIZE = + // solium-disable-next-line indentation + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; + uint256 private constant WORD_LENGTH_BYTES = 0x20; + + // (base^exponent) % FIELD_SIZE + // Cribbed from https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 + function bigModExp(uint256 base, uint256 exponent) internal view returns (uint256 exponentiation) { + uint256 callResult; + uint256[6] memory bigModExpContractInputs; + bigModExpContractInputs[0] = WORD_LENGTH_BYTES; // Length of base + bigModExpContractInputs[1] = WORD_LENGTH_BYTES; // Length of exponent + bigModExpContractInputs[2] = WORD_LENGTH_BYTES; // Length of modulus + bigModExpContractInputs[3] = base; + bigModExpContractInputs[4] = exponent; + bigModExpContractInputs[5] = FIELD_SIZE; + uint256[1] memory output; + assembly { + // solhint-disable-line no-inline-assembly + callResult := staticcall( + not(0), // Gas cost: no limit + 0x05, // Bigmodexp contract address + bigModExpContractInputs, + 0xc0, // Length of input segment: 6*0x20-bytes + output, + 0x20 // Length of output segment + ) + } + if (callResult == 0) { + revert("bigModExp failure!"); + } + return output[0]; + } + + // Let q=FIELD_SIZE. q % 4 = 3, ∴ x≡r^2 mod q ⇒ x^SQRT_POWER≡±r mod q. See + // https://en.wikipedia.org/wiki/Modular_square_root#Prime_or_prime_power_modulus + uint256 private constant SQRT_POWER = (FIELD_SIZE + 1) >> 2; + + // Computes a s.t. a^2 = x in the field. Assumes a exists + function squareRoot(uint256 x) internal view returns (uint256) { + return bigModExp(x, SQRT_POWER); + } + + // The value of y^2 given that (x,y) is on secp256k1. + function ySquared(uint256 x) internal pure returns (uint256) { + // Curve is y^2=x^3+7. See section 2.4.1 of https://www.secg.org/sec2-v2.pdf + uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE); + return addmod(xCubed, 7, FIELD_SIZE); + } + + // True iff p is on secp256k1 + function isOnCurve(uint256[2] memory p) internal pure returns (bool) { + // Section 2.3.6. in https://www.secg.org/sec1-v2.pdf + // requires each ordinate to be in [0, ..., FIELD_SIZE-1] + require(p[0] < FIELD_SIZE, "invalid x-ordinate"); + require(p[1] < FIELD_SIZE, "invalid y-ordinate"); + return ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE); + } + + // Hash x uniformly into {0, ..., FIELD_SIZE-1}. + function fieldHash(bytes memory b) internal pure returns (uint256 x_) { + x_ = uint256(keccak256(b)); + // Rejecting if x >= FIELD_SIZE corresponds to step 2.1 in section 2.3.4 of + // http://www.secg.org/sec1-v2.pdf , which is part of the definition of + // string_to_point in the IETF draft + while (x_ >= FIELD_SIZE) { + x_ = uint256(keccak256(abi.encodePacked(x_))); + } + } + + // Hash b to a random point which hopefully lies on secp256k1. The y ordinate + // is always even, due to + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + // step 5.C, which references arbitrary_string_to_point, defined in + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 as + // returning the point with given x ordinate, and even y ordinate. + function newCandidateSecp256k1Point(bytes memory b) internal view returns (uint256[2] memory p) { + unchecked { + p[0] = fieldHash(b); + p[1] = squareRoot(ySquared(p[0])); + if (p[1] % 2 == 1) { + // Note that 0 <= p[1] < FIELD_SIZE + // so this cannot wrap, we use unchecked to save gas. + p[1] = FIELD_SIZE - p[1]; + } + } + } + + // Domain-separation tag for initial hash in hashToCurve. Corresponds to + // vrf.go/hashToCurveHashPrefix + uint256 internal constant HASH_TO_CURVE_HASH_PREFIX = 1; + + // Cryptographic hash function onto the curve. + // + // Corresponds to algorithm in section 5.4.1.1 of the draft standard. (But see + // DESIGN NOTES above for slight differences.) + // + // TODO(alx): Implement a bounded-computation hash-to-curve, as described in + // "Construction of Rational Points on Elliptic Curves over Finite Fields" + // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.831.5299&rep=rep1&type=pdf + // and suggested by + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-01#section-5.2.2 + // (Though we can't used exactly that because secp256k1's j-invariant is 0.) + // + // This would greatly simplify the analysis in "OTHER SECURITY CONSIDERATIONS" + // https://www.pivotaltracker.com/story/show/171120900 + function hashToCurve(uint256[2] memory pk, uint256 input) internal view returns (uint256[2] memory rv) { + rv = newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, pk, input)); + while (!isOnCurve(rv)) { + rv = newCandidateSecp256k1Point(abi.encodePacked(rv[0])); + } + } + + /** ********************************************************************* + * @notice Check that product==scalar*multiplicand + * + * @dev Based on Vitalik Buterin's idea in ethresear.ch post cited below. + * + * @param multiplicand: secp256k1 point + * @param scalar: non-zero GF(GROUP_ORDER) scalar + * @param product: secp256k1 expected to be multiplier * multiplicand + * @return verifies true iff product==scalar*multiplicand, with cryptographically high probability + */ + function ecmulVerify( + uint256[2] memory multiplicand, + uint256 scalar, + uint256[2] memory product + ) internal pure returns (bool verifies) { + require(scalar != 0, "zero scalar"); // Rules out an ecrecover failure case + uint256 x = multiplicand[0]; // x ordinate of multiplicand + uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28; // parity of y ordinate + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // Point corresponding to address ecrecover(0, v, x, s=scalar*x) is + // (x⁻¹ mod GROUP_ORDER) * (scalar * x * multiplicand - 0 * g), i.e. + // scalar*multiplicand. See https://crypto.stackexchange.com/a/18106 + bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER)); + address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX); + // Explicit conversion to address takes bottom 160 bits + address expected = address(uint160(uint256(keccak256(abi.encodePacked(product))))); + return (actual == expected); + } + + // Returns x1/z1-x2/z2=(x1z2-x2z1)/(z1z2) in projective coordinates on P¹(𝔽ₙ) + function projectiveSub( + uint256 x1, + uint256 z1, + uint256 x2, + uint256 z2 + ) internal pure returns (uint256 x3, uint256 z3) { + unchecked { + uint256 num1 = mulmod(z2, x1, FIELD_SIZE); + // Note this cannot wrap since x2 is a point in [0, FIELD_SIZE-1] + // we use unchecked to save gas. + uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); + (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + } + + // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) + function projectiveMul( + uint256 x1, + uint256 z1, + uint256 x2, + uint256 z2 + ) internal pure returns (uint256 x3, uint256 z3) { + (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + + /** ************************************************************************** + @notice Computes elliptic-curve sum, in projective co-ordinates + + @dev Using projective coordinates avoids costly divisions + + @dev To use this with p and q in affine coordinates, call + @dev projectiveECAdd(px, py, qx, qy). This will return + @dev the addition of (px, py, 1) and (qx, qy, 1), in the + @dev secp256k1 group. + + @dev This can be used to calculate the z which is the inverse to zInv + @dev in isValidVRFOutput. But consider using a faster + @dev re-implementation such as ProjectiveECAdd in the golang vrf package. + + @dev This function assumes [px,py,1],[qx,qy,1] are valid projective + coordinates of secp256k1 points. That is safe in this contract, + because this method is only used by linearCombination, which checks + points are on the curve via ecrecover. + ************************************************************************** + @param px The first affine coordinate of the first summand + @param py The second affine coordinate of the first summand + @param qx The first affine coordinate of the second summand + @param qy The second affine coordinate of the second summand + + (px,py) and (qx,qy) must be distinct, valid secp256k1 points. + ************************************************************************** + Return values are projective coordinates of [px,py,1]+[qx,qy,1] as points + on secp256k1, in P²(𝔽ₙ) + @return sx + @return sy + @return sz + */ + function projectiveECAdd( + uint256 px, + uint256 py, + uint256 qx, + uint256 qy + ) + internal + pure + returns ( + uint256 sx, + uint256 sy, + uint256 sz + ) + { + unchecked { + // See "Group law for E/K : y^2 = x^3 + ax + b", in section 3.1.2, p. 80, + // "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes and Vanstone + // We take the equations there for (sx,sy), and homogenize them to + // projective coordinates. That way, no inverses are required, here, and we + // only need the one inverse in affineECAdd. + + // We only need the "point addition" equations from Hankerson et al. Can + // skip the "point doubling" equations because p1 == p2 is cryptographically + // impossible, and required not to be the case in linearCombination. + + // Add extra "projective coordinate" to the two points + (uint256 z1, uint256 z2) = (1, 1); + + // (lx, lz) = (qy-py)/(qx-px), i.e., gradient of secant line. + // Cannot wrap since px and py are in [0, FIELD_SIZE-1] + uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE); + uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE); + + uint256 dx; // Accumulates denominator from sx calculation + // sx=((qy-py)/(qx-px))^2-px-qx + (sx, dx) = projectiveMul(lx, lz, lx, lz); // ((qy-py)/(qx-px))^2 + (sx, dx) = projectiveSub(sx, dx, px, z1); // ((qy-py)/(qx-px))^2-px + (sx, dx) = projectiveSub(sx, dx, qx, z2); // ((qy-py)/(qx-px))^2-px-qx + + uint256 dy; // Accumulates denominator from sy calculation + // sy=((qy-py)/(qx-px))(px-sx)-py + (sy, dy) = projectiveSub(px, z1, sx, dx); // px-sx + (sy, dy) = projectiveMul(sy, dy, lx, lz); // ((qy-py)/(qx-px))(px-sx) + (sy, dy) = projectiveSub(sy, dy, py, z1); // ((qy-py)/(qx-px))(px-sx)-py + + if (dx != dy) { + // Cross-multiply to put everything over a common denominator + sx = mulmod(sx, dy, FIELD_SIZE); + sy = mulmod(sy, dx, FIELD_SIZE); + sz = mulmod(dx, dy, FIELD_SIZE); + } else { + // Already over a common denominator, use that for z ordinate + sz = dx; + } + } + } + + // p1+p2, as affine points on secp256k1. + // + // invZ must be the inverse of the z returned by projectiveECAdd(p1, p2). + // It is computed off-chain to save gas. + // + // p1 and p2 must be distinct, because projectiveECAdd doesn't handle + // point doubling. + function affineECAdd( + uint256[2] memory p1, + uint256[2] memory p2, + uint256 invZ + ) internal pure returns (uint256[2] memory) { + uint256 x; + uint256 y; + uint256 z; + (x, y, z) = projectiveECAdd(p1[0], p1[1], p2[0], p2[1]); + require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z"); + // Clear the z ordinate of the projective representation by dividing through + // by it, to obtain the affine representation + return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)]; + } + + // True iff address(c*p+s*g) == lcWitness, where g is generator. (With + // cryptographically high probability.) + function verifyLinearCombinationWithGenerator( + uint256 c, + uint256[2] memory p, + uint256 s, + address lcWitness + ) internal pure returns (bool) { + // Rule out ecrecover failure modes which return address 0. + unchecked { + require(lcWitness != address(0), "bad witness"); + uint8 v = (p[1] % 2 == 0) ? 27 : 28; // parity of y-ordinate of p + // Note this cannot wrap (X - Y % X), but we use unchecked to save + // gas. + bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER)); // -s*p[0] + bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER)); // c*p[0] + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // The point corresponding to the address returned by + // ecrecover(-s*p[0],v,p[0],c*p[0]) is + // (p[0]⁻¹ mod GROUP_ORDER)*(c*p[0]-(-s)*p[0]*g)=c*p+s*g. + // See https://crypto.stackexchange.com/a/18106 + // https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v + address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature); + return computed == lcWitness; + } + } + + // c*p1 + s*p2. Requires cp1Witness=c*p1 and sp2Witness=s*p2. Also + // requires cp1Witness != sp2Witness (which is fine for this application, + // since it is cryptographically impossible for them to be equal. In the + // (cryptographically impossible) case that a prover accidentally derives + // a proof with equal c*p1 and s*p2, they should retry with a different + // proof nonce.) Assumes that all points are on secp256k1 + // (which is checked in verifyVRFProof below.) + function linearCombination( + uint256 c, + uint256[2] memory p1, + uint256[2] memory cp1Witness, + uint256 s, + uint256[2] memory p2, + uint256[2] memory sp2Witness, + uint256 zInv + ) internal pure returns (uint256[2] memory) { + unchecked { + // Note we are relying on the wrap around here + require((cp1Witness[0] % FIELD_SIZE) != (sp2Witness[0] % FIELD_SIZE), "points in sum must be distinct"); + require(ecmulVerify(p1, c, cp1Witness), "First mul check failed"); + require(ecmulVerify(p2, s, sp2Witness), "Second mul check failed"); + return affineECAdd(cp1Witness, sp2Witness, zInv); + } + } + + // Domain-separation tag for the hash taken in scalarFromCurvePoints. + // Corresponds to scalarFromCurveHashPrefix in vrf.go + uint256 internal constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2; + + // Pseudo-random number from inputs. Matches vrf.go/scalarFromCurvePoints, and + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + // The draft calls (in step 7, via the definition of string_to_int, in + // https://datatracker.ietf.org/doc/html/rfc8017#section-4.2 ) for taking the + // first hash without checking that it corresponds to a number less than the + // group order, which will lead to a slight bias in the sample. + // + // TODO(alx): We could save a bit of gas by following the standard here and + // using the compressed representation of the points, if we collated the y + // parities into a single bytes32. + // https://www.pivotaltracker.com/story/show/171120588 + function scalarFromCurvePoints( + uint256[2] memory hash, + uint256[2] memory pk, + uint256[2] memory gamma, + address uWitness, + uint256[2] memory v + ) internal pure returns (uint256 s) { + return uint256(keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, hash, pk, gamma, v, uWitness))); + } + + // True if (gamma, c, s) is a correctly constructed randomness proof from pk + // and seed. zInv must be the inverse of the third ordinate from + // projectiveECAdd applied to cGammaWitness and sHashWitness. Corresponds to + // section 5.3 of the IETF draft. + // + // TODO(alx): Since I'm only using pk in the ecrecover call, I could only pass + // the x ordinate, and the parity of the y ordinate in the top bit of uWitness + // (which I could make a uint256 without using any extra space.) Would save + // about 2000 gas. https://www.pivotaltracker.com/story/show/170828567 + function verifyVRFProof( + uint256[2] memory pk, + uint256[2] memory gamma, + uint256 c, + uint256 s, + uint256 seed, + address uWitness, + uint256[2] memory cGammaWitness, + uint256[2] memory sHashWitness, + uint256 zInv + ) internal view { + unchecked { + require(isOnCurve(pk), "public key is not on curve"); + require(isOnCurve(gamma), "gamma is not on curve"); + require(isOnCurve(cGammaWitness), "cGammaWitness is not on curve"); + require(isOnCurve(sHashWitness), "sHashWitness is not on curve"); + // Step 5. of IETF draft section 5.3 (pk corresponds to 5.3's Y, and here + // we use the address of u instead of u itself. Also, here we add the + // terms instead of taking the difference, and in the proof construction in + // vrf.GenerateProof, we correspondingly take the difference instead of + // taking the sum as they do in step 7 of section 5.1.) + require(verifyLinearCombinationWithGenerator(c, pk, s, uWitness), "addr(c*pk+s*g)!=_uWitness"); + // Step 4. of IETF draft section 5.3 (pk corresponds to Y, seed to alpha_string) + uint256[2] memory hash = hashToCurve(pk, seed); + // Step 6. of IETF draft section 5.3, but see note for step 5 about +/- terms + uint256[2] memory v = linearCombination(c, gamma, cGammaWitness, s, hash, sHashWitness, zInv); + // Steps 7. and 8. of IETF draft section 5.3 + uint256 derivedC = scalarFromCurvePoints(hash, pk, gamma, uWitness, v); + require(c == derivedC, "invalid proof"); + } + } + + // Domain-separation tag for the hash used as the final VRF output. + // Corresponds to vrfRandomOutputHashPrefix in vrf.go + uint256 internal constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3; + + struct Proof { + uint256[2] pk; + uint256[2] gamma; + uint256 c; + uint256 s; + uint256 seed; + address uWitness; + uint256[2] cGammaWitness; + uint256[2] sHashWitness; + uint256 zInv; + } + + /* *************************************************************************** + * @notice Returns proof's output, if proof is valid. Otherwise reverts + + * @param proof vrf proof components + * @param seed seed used to generate the vrf output + * + * Throws if proof is invalid, otherwise: + * @return output i.e., the random output implied by the proof + * *************************************************************************** + */ + function randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) { + verifyVRFProof( + proof.pk, + proof.gamma, + proof.c, + proof.s, + seed, + proof.uWitness, + proof.cGammaWitness, + proof.sHashWitness, + proof.zInv + ); + output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma))); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFConsumerBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFConsumerBase.sol new file mode 100644 index 0000000..e75ec2d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFConsumerBase.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; + +import "./VRFRequestIDBase.sol"; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constuctor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator, _link) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash), and have told you the minimum LINK + * @dev price for VRF service. Make sure your contract has sufficient LINK, and + * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you + * @dev want to generate randomness from. + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomness method. + * + * @dev The randomness argument to fulfillRandomness is the actual random value + * @dev generated from your seed. + * + * @dev The requestId argument is generated from the keyHash and the seed by + * @dev makeRequestId(keyHash, seed). If your contract could have concurrent + * @dev requests open, you can use the requestId to track which seed is + * @dev associated with which randomness. See VRFRequestIDBase.sol for more + * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously.) + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. (Which is critical to making unpredictable randomness! See the + * @dev next section.) + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the ultimate input to the VRF is mixed with the block hash of the + * @dev block in which the request is made, user-provided seeds have no impact + * @dev on its economic security properties. They are only included for API + * @dev compatability with previous versions of this contract. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. + */ +abstract contract VRFConsumerBase is VRFRequestIDBase { + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBase expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomness the VRF output + */ + function fulfillRandomness( + bytes32 requestId, + uint256 randomness + ) + internal + virtual; + + /** + * @dev In order to keep backwards compatibility we have kept the user + * seed field around. We remove the use of it because given that the blockhash + * enters later, it overrides whatever randomness the used seed provides. + * Given that it adds no security, and can easily lead to misunderstandings, + * we have removed it from usage and can now provide a simpler API. + */ + uint256 constant private USER_SEED_PLACEHOLDER = 0; + + /** + * @notice requestRandomness initiates a request for VRF output given _seed + * + * @dev The fulfillRandomness method receives the output, once it's provided + * @dev by the Oracle, and verified by the vrfCoordinator. + * + * @dev The _keyHash must already be registered with the VRFCoordinator, and + * @dev the _fee must exceed the fee specified during registration of the + * @dev _keyHash. + * + * @dev The _seed parameter is vestigial, and is kept only for API + * @dev compatibility with older versions. It can't *hurt* to mix in some of + * @dev your own randomness, here, but it's not necessary because the VRF + * @dev oracle will mix the hash of the block containing your request into the + * @dev VRF seed it ultimately uses. + * + * @param _keyHash ID of public key against which randomness is generated + * @param _fee The amount of LINK to send with the request + * + * @return requestId unique ID for this request + * + * @dev The returned requestId can be used to distinguish responses to + * @dev concurrent requests. It is passed as the first argument to + * @dev fulfillRandomness. + */ + function requestRandomness( + bytes32 _keyHash, + uint256 _fee + ) + internal + returns ( + bytes32 requestId + ) + { + LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); + // This is the seed passed to VRFCoordinator. The oracle will mix this with + // the hash of the block containing this request to obtain the seed/input + // which is finally passed to the VRF cryptographic machinery. + uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); + // nonces[_keyHash] must stay in sync with + // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above + // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). + // This provides protection against the user repeating their input seed, + // which would result in a predictable/duplicate output, if multiple such + // requests appeared in the same block. + nonces[_keyHash] = nonces[_keyHash] + 1; + return makeRequestId(_keyHash, vRFSeed); + } + + LinkTokenInterface immutable internal LINK; + address immutable private vrfCoordinator; + + // Nonces for each VRF key from which randomness has been requested. + // + // Must stay in sync with VRFCoordinator[_keyHash][this] + mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + * @param _link address of LINK token contract + * + * @dev https://docs.chain.link/docs/link-token-contracts + */ + constructor( + address _vrfCoordinator, + address _link + ) { + vrfCoordinator = _vrfCoordinator; + LINK = LinkTokenInterface(_link); + } + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomness( + bytes32 requestId, + uint256 randomness + ) + external + { + require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); + fulfillRandomness(requestId, randomness); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFConsumerBaseV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFConsumerBaseV2.sol new file mode 100644 index 0000000..3d4036f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFConsumerBaseV2.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. It ensures 2 things: + * @dev 1. The fulfillment came from the VRFCoordinator + * @dev 2. The consumer contract implements fulfillRandomWords. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constuctor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash). Create subscription, fund it + * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface + * @dev subscription management functions). + * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations, + * @dev callbackGasLimit, numWords), + * @dev see (VRFCoordinatorInterface for a description of the arguments). + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomWords method. + * + * @dev The randomness argument to fulfillRandomWords is a set of random words + * @dev generated from your requestId and the blockHash of the request. + * + * @dev If your contract could have concurrent requests open, you can use the + * @dev requestId returned from requestRandomWords to track which response is associated + * @dev with which randomness request. + * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously. + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. It is for this reason that + * @dev that you can signal to an oracle you'd like them to wait longer before + * @dev responding to the request (however this is not enforced in the contract + * @dev and so remains effective only in the case of unmodified oracle software). + */ +abstract contract VRFConsumerBaseV2 { + error OnlyCoordinatorCanFulfill(address have, address want); + address private immutable vrfCoordinator; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + */ + constructor(address _vrfCoordinator) { + vrfCoordinator = _vrfCoordinator; + } + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomWords the VRF output expanded to the requested number of words + */ + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual; + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external { + if (msg.sender != vrfCoordinator) { + revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator); + } + fulfillRandomWords(requestId, randomWords); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFCoordinatorV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFCoordinatorV2.sol new file mode 100644 index 0000000..38cf88c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFCoordinatorV2.sol @@ -0,0 +1,880 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/BlockhashStoreInterface.sol"; +import "../interfaces/AggregatorV3Interface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../interfaces/TypeAndVersionInterface.sol"; +import "../interfaces/ERC677ReceiverInterface.sol"; +import "./VRF.sol"; +import "../ConfirmedOwner.sol"; +import "../VRFConsumerBaseV2.sol"; + +contract VRFCoordinatorV2 is + VRF, + ConfirmedOwner, + TypeAndVersionInterface, + VRFCoordinatorV2Interface, + ERC677ReceiverInterface +{ + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + BlockhashStoreInterface public immutable BLOCKHASH_STORE; + + // We need to maintain a list of consuming addresses. + // This bound ensures we are able to loop over them as needed. + // Should a user require more consumers, they can use multiple subscriptions. + uint16 public constant MAX_CONSUMERS = 100; + error TooManyConsumers(); + error InsufficientBalance(); + error InvalidConsumer(uint64 subId, address consumer); + error InvalidSubscription(); + error OnlyCallableFromLink(); + error InvalidCalldata(); + error MustBeSubOwner(address owner); + error PendingRequestExists(); + error MustBeRequestedOwner(address proposedOwner); + error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen + event FundsRecovered(address to, uint256 amount); + // We use the subscription struct (1 word) + // at fulfillment time. + struct Subscription { + // There are only 1e9*1e18 = 1e27 juels in existence, so the balance can fit in uint96 (2^96 ~ 7e28) + uint96 balance; // Common link balance used for all consumer requests. + uint64 reqCount; // For fee tiers + } + // We use the config for the mgmt APIs + struct SubscriptionConfig { + address owner; // Owner can fund/withdraw/cancel the sub. + address requestedOwner; // For safely transferring sub ownership. + // Maintains the list of keys in s_consumers. + // We do this for 2 reasons: + // 1. To be able to clean up all keys from s_consumers when canceling a subscription. + // 2. To be able to return the list of all consumers in getSubscription. + // Note that we need the s_consumers map to be able to directly check if a + // consumer is valid without reading all the consumers from storage. + address[] consumers; + } + // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. + mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subId */ /* nonce */ + private s_consumers; + mapping(uint64 => SubscriptionConfig) /* subId */ /* subscriptionConfig */ + private s_subscriptionConfigs; + mapping(uint64 => Subscription) /* subId */ /* subscription */ + private s_subscriptions; + // We make the sub count public so that its possible to + // get all the current subscriptions via getSubscription. + uint64 private s_currentSubId; + // s_totalBalance tracks the total link sent to/from + // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. + // A discrepancy with this contract's link balance indicates someone + // sent tokens using transfer and so we may need to use recoverFunds. + uint96 private s_totalBalance; + event SubscriptionCreated(uint64 indexed subId, address owner); + event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); + event SubscriptionConsumerAdded(uint64 indexed subId, address consumer); + event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer); + event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); + event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to); + event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to); + + // Set this maximum to 200 to give us a 56 block window to fulfill + // the request before requiring the block hash feeder. + uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; + uint32 public constant MAX_NUM_WORDS = 500; + // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) + // and some arithmetic operations. + uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; + error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); + error GasLimitTooBig(uint32 have, uint32 want); + error NumWordsTooBig(uint32 have, uint32 want); + error ProvingKeyAlreadyRegistered(bytes32 keyHash); + error NoSuchProvingKey(bytes32 keyHash); + error InvalidLinkWeiPrice(int256 linkWei); + error InsufficientGasForConsumer(uint256 have, uint256 want); + error NoCorrespondingRequest(); + error IncorrectCommitment(); + error BlockhashNotInStore(uint256 blockNum); + error PaymentTooLarge(); + error Reentrant(); + struct RequestCommitment { + uint64 blockNum; + uint64 subId; + uint32 callbackGasLimit; + uint32 numWords; + address sender; + } + mapping(bytes32 => address) /* keyHash */ /* oracle */ + private s_provingKeys; + bytes32[] private s_provingKeyHashes; + mapping(address => uint96) /* oracle */ /* LINK balance */ + private s_withdrawableTokens; + mapping(uint256 => bytes32) /* requestID */ /* commitment */ + private s_requestCommitments; + event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); + event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle); + event RandomWordsRequested( + bytes32 indexed keyHash, + uint256 requestId, + uint256 preSeed, + uint64 indexed subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + address indexed sender + ); + event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); + + struct Config { + uint16 minimumRequestConfirmations; + uint32 maxGasLimit; + // Reentrancy protection. + bool reentrancyLock; + // stalenessSeconds is how long before we consider the feed price to be stale + // and fallback to fallbackWeiPerUnitLink. + uint32 stalenessSeconds; + // Gas to cover oracle payment after we calculate the payment. + // We make it configurable in case those operations are repriced. + uint32 gasAfterPaymentCalculation; + } + int256 private s_fallbackWeiPerUnitLink; + Config private s_config; + FeeConfig private s_feeConfig; + struct FeeConfig { + // Flat fee charged per fulfillment in millionths of link + // So fee range is [0, 2^32/10^6]. + uint32 fulfillmentFlatFeeLinkPPMTier1; + uint32 fulfillmentFlatFeeLinkPPMTier2; + uint32 fulfillmentFlatFeeLinkPPMTier3; + uint32 fulfillmentFlatFeeLinkPPMTier4; + uint32 fulfillmentFlatFeeLinkPPMTier5; + uint24 reqsForTier2; + uint24 reqsForTier3; + uint24 reqsForTier4; + uint24 reqsForTier5; + } + event ConfigSet( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + FeeConfig feeConfig + ); + + constructor( + address link, + address blockhashStore, + address linkEthFeed + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + BLOCKHASH_STORE = BlockhashStoreInterface(blockhashStore); + } + + /** + * @notice Registers a proving key to an oracle. + * @param oracle address of the oracle + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + if (s_provingKeys[kh] != address(0)) { + revert ProvingKeyAlreadyRegistered(kh); + } + s_provingKeys[kh] = oracle; + s_provingKeyHashes.push(kh); + emit ProvingKeyRegistered(kh, oracle); + } + + /** + * @notice Deregisters a proving key to an oracle. + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + address oracle = s_provingKeys[kh]; + if (oracle == address(0)) { + revert NoSuchProvingKey(kh); + } + delete s_provingKeys[kh]; + for (uint256 i = 0; i < s_provingKeyHashes.length; i++) { + if (s_provingKeyHashes[i] == kh) { + bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1]; + // Copy last element and overwrite kh to be deleted with it + s_provingKeyHashes[i] = last; + s_provingKeyHashes.pop(); + } + } + emit ProvingKeyDeregistered(kh, oracle); + } + + /** + * @notice Returns the proving key hash key associated with this public key + * @param publicKey the key to return the hash of + */ + function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { + return keccak256(abi.encode(publicKey)); + } + + /** + * @notice Sets the configuration of the vrfv2 coordinator + * @param minimumRequestConfirmations global min for request confirmations + * @param maxGasLimit global max for request gas limit + * @param stalenessSeconds if the eth/link feed is more stale then this, use the fallback price + * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement + * @param fallbackWeiPerUnitLink fallback eth/link price in the case of a stale feed + * @param feeConfig fee tier configuration + */ + function setConfig( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + FeeConfig memory feeConfig + ) external onlyOwner { + if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { + revert InvalidRequestConfirmations( + minimumRequestConfirmations, + minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + if (fallbackWeiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink); + } + s_config = Config({ + minimumRequestConfirmations: minimumRequestConfirmations, + maxGasLimit: maxGasLimit, + stalenessSeconds: stalenessSeconds, + gasAfterPaymentCalculation: gasAfterPaymentCalculation, + reentrancyLock: false + }); + s_feeConfig = feeConfig; + s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; + emit ConfigSet( + minimumRequestConfirmations, + maxGasLimit, + stalenessSeconds, + gasAfterPaymentCalculation, + fallbackWeiPerUnitLink, + s_feeConfig + ); + } + + function getConfig() + external + view + returns ( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation + ) + { + return ( + s_config.minimumRequestConfirmations, + s_config.maxGasLimit, + s_config.stalenessSeconds, + s_config.gasAfterPaymentCalculation + ); + } + + function getFeeConfig() + external + view + returns ( + uint32 fulfillmentFlatFeeLinkPPMTier1, + uint32 fulfillmentFlatFeeLinkPPMTier2, + uint32 fulfillmentFlatFeeLinkPPMTier3, + uint32 fulfillmentFlatFeeLinkPPMTier4, + uint32 fulfillmentFlatFeeLinkPPMTier5, + uint24 reqsForTier2, + uint24 reqsForTier3, + uint24 reqsForTier4, + uint24 reqsForTier5 + ) + { + return ( + s_feeConfig.fulfillmentFlatFeeLinkPPMTier1, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier2, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier3, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier4, + s_feeConfig.fulfillmentFlatFeeLinkPPMTier5, + s_feeConfig.reqsForTier2, + s_feeConfig.reqsForTier3, + s_feeConfig.reqsForTier4, + s_feeConfig.reqsForTier5 + ); + } + + function getTotalBalance() external view returns (uint256) { + return s_totalBalance; + } + + function getFallbackWeiPerUnitLink() external view returns (int256) { + return s_fallbackWeiPerUnitLink; + } + + /** + * @notice Owner cancel subscription, sends remaining link directly to the subscription owner. + * @param subId subscription id + * @dev notably can be called even if there are pending requests, outstanding ones may fail onchain + */ + function ownerCancelSubscription(uint64 subId) external onlyOwner { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner); + } + + /** + * @notice Recover link sent with transfer instead of transferAndCall. + * @param to address to send link to + */ + function recoverFunds(address to) external onlyOwner { + uint256 externalBalance = LINK.balanceOf(address(this)); + uint256 internalBalance = uint256(s_totalBalance); + if (internalBalance > externalBalance) { + revert BalanceInvariantViolated(internalBalance, externalBalance); + } + if (internalBalance < externalBalance) { + uint256 amount = externalBalance - internalBalance; + LINK.transfer(to, amount); + emit FundsRecovered(to, amount); + } + // If the balances are equal, nothing to be done. + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function getRequestConfig() + external + view + override + returns ( + uint16, + uint32, + bytes32[] memory + ) + { + return (s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_provingKeyHashes); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function requestRandomWords( + bytes32 keyHash, + uint64 subId, + uint16 requestConfirmations, + uint32 callbackGasLimit, + uint32 numWords + ) external override nonReentrant returns (uint256) { + // Input validation using the subscription storage. + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // Its important to ensure that the consumer is in fact who they say they + // are, otherwise they could use someone else's subscription balance. + // A nonce of 0 indicates consumer is not allocated to the sub. + uint64 currentNonce = s_consumers[msg.sender][subId]; + if (currentNonce == 0) { + revert InvalidConsumer(subId, msg.sender); + } + // Input validation using the config storage word. + if ( + requestConfirmations < s_config.minimumRequestConfirmations || requestConfirmations > MAX_REQUEST_CONFIRMATIONS + ) { + revert InvalidRequestConfirmations( + requestConfirmations, + s_config.minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + // No lower bound on the requested gas limit. A user could request 0 + // and they would simply be billed for the proof verification and wouldn't be + // able to do anything with the random value. + if (callbackGasLimit > s_config.maxGasLimit) { + revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit); + } + if (numWords > MAX_NUM_WORDS) { + revert NumWordsTooBig(numWords, MAX_NUM_WORDS); + } + // Note we do not check whether the keyHash is valid to save gas. + // The consequence for users is that they can send requests + // for invalid keyHashes which will simply not be fulfilled. + uint64 nonce = currentNonce + 1; + (uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, subId, nonce); + + s_requestCommitments[requestId] = keccak256( + abi.encode(requestId, block.number, subId, callbackGasLimit, numWords, msg.sender) + ); + emit RandomWordsRequested( + keyHash, + requestId, + preSeed, + subId, + requestConfirmations, + callbackGasLimit, + numWords, + msg.sender + ); + s_consumers[msg.sender][subId] = nonce; + + return requestId; + } + + /** + * @notice Get request commitment + * @param requestId id of request + * @dev used to determine if a request is fulfilled or not + */ + function getCommitment(uint256 requestId) external view returns (bytes32) { + return s_requestCommitments[requestId]; + } + + function computeRequestId( + bytes32 keyHash, + address sender, + uint64 subId, + uint64 nonce + ) private pure returns (uint256, uint256) { + uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); + return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available. + */ + function callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + // solhint-disable-next-line no-inline-assembly + assembly { + let g := gas() + // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow + // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, GAS_FOR_CALL_EXACT_CHECK) { + revert(0, 0) + } + g := sub(g, GAS_FOR_CALL_EXACT_CHECK) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + function getRandomnessFromProof(Proof memory proof, RequestCommitment memory rc) + private + view + returns ( + bytes32 keyHash, + uint256 requestId, + uint256 randomness + ) + { + keyHash = hashOfKey(proof.pk); + // Only registered proving keys are permitted. + address oracle = s_provingKeys[keyHash]; + if (oracle == address(0)) { + revert NoSuchProvingKey(keyHash); + } + requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); + bytes32 commitment = s_requestCommitments[requestId]; + if (commitment == 0) { + revert NoCorrespondingRequest(); + } + if ( + commitment != keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender)) + ) { + revert IncorrectCommitment(); + } + + bytes32 blockHash = blockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + blockHash = BLOCKHASH_STORE.getBlockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + revert BlockhashNotInStore(rc.blockNum); + } + } + + // The seed actually used by the VRF machinery, mixing in the blockhash + uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); + randomness = VRF.randomValueFromVRFProof(proof, actualSeed); // Reverts on failure + } + + /* + * @notice Compute fee based on the request count + * @param reqCount number of requests + * @return feePPM fee in LINK PPM + */ + function getFeeTier(uint64 reqCount) public view returns (uint32) { + FeeConfig memory fc = s_feeConfig; + if (0 <= reqCount && reqCount <= fc.reqsForTier2) { + return fc.fulfillmentFlatFeeLinkPPMTier1; + } + if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { + return fc.fulfillmentFlatFeeLinkPPMTier2; + } + if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { + return fc.fulfillmentFlatFeeLinkPPMTier3; + } + if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { + return fc.fulfillmentFlatFeeLinkPPMTier4; + } + return fc.fulfillmentFlatFeeLinkPPMTier5; + } + + /* + * @notice Fulfill a randomness request + * @param proof contains the proof and randomness + * @param rc request commitment pre-image, committed to at request time + * @return payment amount billed to the subscription + * @dev simulated offchain to determine if sufficient balance is present to fulfill the request + */ + function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint96) { + uint256 startGas = gasleft(); + (bytes32 keyHash, uint256 requestId, uint256 randomness) = getRandomnessFromProof(proof, rc); + + uint256[] memory randomWords = new uint256[](rc.numWords); + for (uint256 i = 0; i < rc.numWords; i++) { + randomWords[i] = uint256(keccak256(abi.encode(randomness, i))); + } + + delete s_requestCommitments[requestId]; + VRFConsumerBaseV2 v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, requestId, randomWords); + // Call with explicitly the amount of callback gas requested + // Important to not let them exhaust the gas budget and avoid oracle payment. + // Do not allow any non-view/non-pure coordinator functions to be called + // during the consumers callback code via reentrancyLock. + // Note that callWithExactGas will revert if we do not have sufficient gas + // to give the callee their requested amount. + s_config.reentrancyLock = true; + bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp); + s_config.reentrancyLock = false; + + // Increment the req count for fee tier selection. + uint64 reqCount = s_subscriptions[rc.subId].reqCount; + s_subscriptions[rc.subId].reqCount += 1; + + // We want to charge users exactly for how much gas they use in their callback. + // The gasAfterPaymentCalculation is meant to cover these additional operations where we + // decrement the subscription balance and increment the oracles withdrawable balance. + // We also add the flat link fee to the payment amount. + // Its specified in millionths of link, if s_config.fulfillmentFlatFeeLinkPPM = 1 + // 1 link / 1e6 = 1e18 juels / 1e6 = 1e12 juels. + uint96 payment = calculatePaymentAmount( + startGas, + s_config.gasAfterPaymentCalculation, + getFeeTier(reqCount), + tx.gasprice + ); + if (s_subscriptions[rc.subId].balance < payment) { + revert InsufficientBalance(); + } + s_subscriptions[rc.subId].balance -= payment; + s_withdrawableTokens[s_provingKeys[keyHash]] += payment; + // Include payment in the event for tracking costs. + emit RandomWordsFulfilled(requestId, randomness, payment, success); + return payment; + } + + // Get the amount of gas used for fulfillment + function calculatePaymentAmount( + uint256 startGas, + uint256 gasAfterPaymentCalculation, + uint32 fulfillmentFlatFeeLinkPPM, + uint256 weiPerUnitGas + ) internal view returns (uint96) { + int256 weiPerUnitLink; + weiPerUnitLink = getFeedData(); + if (weiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(weiPerUnitLink); + } + // (1e18 juels/link) (wei/gas * gas) / (wei/link) = juels + uint256 paymentNoFee = (1e18 * weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft())) / + uint256(weiPerUnitLink); + uint256 fee = 1e12 * uint256(fulfillmentFlatFeeLinkPPM); + if (paymentNoFee > (1e27 - fee)) { + revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. + } + return uint96(paymentNoFee + fee); + } + + function getFeedData() private view returns (int256) { + uint32 stalenessSeconds = s_config.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 weiPerUnitLink; + (, weiPerUnitLink, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + // solhint-disable-next-line not-rely-on-time + if (staleFallback && stalenessSeconds < block.timestamp - timestamp) { + weiPerUnitLink = s_fallbackWeiPerUnitLink; + } + return weiPerUnitLink; + } + + /* + * @notice Oracle withdraw LINK earned through fulfilling requests + * @param recipient where to send the funds + * @param amount amount to withdraw + */ + function oracleWithdraw(address recipient, uint96 amount) external nonReentrant { + if (s_withdrawableTokens[msg.sender] < amount) { + revert InsufficientBalance(); + } + s_withdrawableTokens[msg.sender] -= amount; + s_totalBalance -= amount; + if (!LINK.transfer(recipient, amount)) { + revert InsufficientBalance(); + } + } + + function onTokenTransfer( + address, /* sender */ + uint256 amount, + bytes calldata data + ) external override nonReentrant { + if (msg.sender != address(LINK)) { + revert OnlyCallableFromLink(); + } + if (data.length != 32) { + revert InvalidCalldata(); + } + uint64 subId = abi.decode(data, (uint64)); + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // We do not check that the msg.sender is the subscription owner, + // anyone can fund a subscription. + uint256 oldBalance = s_subscriptions[subId].balance; + s_subscriptions[subId].balance += uint96(amount); + s_totalBalance += uint96(amount); + emit SubscriptionFunded(subId, oldBalance, oldBalance + amount); + } + + function getCurrentSubId() external view returns (uint64) { + return s_currentSubId; + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function getSubscription(uint64 subId) + external + view + override + returns ( + uint96 balance, + uint64 reqCount, + address owner, + address[] memory consumers + ) + { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + return ( + s_subscriptions[subId].balance, + s_subscriptions[subId].reqCount, + s_subscriptionConfigs[subId].owner, + s_subscriptionConfigs[subId].consumers + ); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function createSubscription() external override nonReentrant returns (uint64) { + s_currentSubId++; + uint64 currentSubId = s_currentSubId; + address[] memory consumers = new address[](0); + s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0}); + s_subscriptionConfigs[currentSubId] = SubscriptionConfig({ + owner: msg.sender, + requestedOwner: address(0), + consumers: consumers + }); + + emit SubscriptionCreated(currentSubId, msg.sender); + return currentSubId; + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) + external + override + onlySubOwner(subId) + nonReentrant + { + // Proposing to address(0) would never be claimable so don't need to check. + if (s_subscriptionConfigs[subId].requestedOwner != newOwner) { + s_subscriptionConfigs[subId].requestedOwner = newOwner; + emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); + } + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function acceptSubscriptionOwnerTransfer(uint64 subId) external override nonReentrant { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) { + revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner); + } + address oldOwner = s_subscriptionConfigs[subId].owner; + s_subscriptionConfigs[subId].owner = msg.sender; + s_subscriptionConfigs[subId].requestedOwner = address(0); + emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function removeConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + if (s_consumers[consumer][subId] == 0) { + revert InvalidConsumer(subId, consumer); + } + // Note bounded by MAX_CONSUMERS + address[] memory consumers = s_subscriptionConfigs[subId].consumers; + uint256 lastConsumerIndex = consumers.length - 1; + for (uint256 i = 0; i < consumers.length; i++) { + if (consumers[i] == consumer) { + address last = consumers[lastConsumerIndex]; + // Storage write to preserve last element + s_subscriptionConfigs[subId].consumers[i] = last; + // Storage remove last element + s_subscriptionConfigs[subId].consumers.pop(); + break; + } + } + delete s_consumers[consumer][subId]; + emit SubscriptionConsumerRemoved(subId, consumer); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + // Already maxed, cannot add any more consumers. + if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) { + revert TooManyConsumers(); + } + if (s_consumers[consumer][subId] != 0) { + // Idempotence - do nothing if already added. + // Ensures uniqueness in s_subscriptions[subId].consumers. + return; + } + // Initialize the nonce to 1, indicating the consumer is allocated. + s_consumers[consumer][subId] = 1; + s_subscriptionConfigs[subId].consumers.push(consumer); + + emit SubscriptionConsumerAdded(subId, consumer); + } + + /** + * @inheritdoc VRFCoordinatorV2Interface + */ + function cancelSubscription(uint64 subId, address to) external override onlySubOwner(subId) nonReentrant { + if (pendingRequestExists(subId)) { + revert PendingRequestExists(); + } + cancelSubscriptionHelper(subId, to); + } + + function cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + Subscription memory sub = s_subscriptions[subId]; + uint96 balance = sub.balance; + // Note bounded by MAX_CONSUMERS; + // If no consumers, does nothing. + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + delete s_consumers[subConfig.consumers[i]][subId]; + } + delete s_subscriptionConfigs[subId]; + delete s_subscriptions[subId]; + s_totalBalance -= balance; + if (!LINK.transfer(to, uint256(balance))) { + revert InsufficientBalance(); + } + emit SubscriptionCanceled(subId, to, balance); + } + + /* + * @noticeCheck to see if there exists a request commitment consumers + * for all consumers and keyhashes for a given sub. + * @param subId where to send the funds + * @return exits true if outstanding requests + * @dev Looping is bounded to MAX_CONSUMERS*(number of keyhashes). + * @dev Used to disable subscription canceling while outstanding request are present. + */ + function pendingRequestExists(uint64 subId) public view returns (bool) { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + for (uint256 j = 0; j < s_provingKeyHashes.length; j++) { + (uint256 reqId, ) = computeRequestId( + s_provingKeyHashes[j], + subConfig.consumers[i], + subId, + s_consumers[subConfig.consumers[i]][subId] + ); + if (s_requestCommitments[reqId] != 0) { + return true; + } + } + } + return false; + } + + modifier onlySubOwner(uint64 subId) { + address owner = s_subscriptionConfigs[subId].owner; + if (owner == address(0)) { + revert InvalidSubscription(); + } + if (msg.sender != owner) { + revert MustBeSubOwner(owner); + } + _; + } + + modifier nonReentrant() { + if (s_config.reentrancyLock) { + revert Reentrant(); + } + _; + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() external pure virtual override returns (string memory) { + return "VRFCoordinatorV2 1.0.0"; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFRequestIDBase.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFRequestIDBase.sol new file mode 100644 index 0000000..2bb73e5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/VRFRequestIDBase.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract VRFRequestIDBase { + + /** + * @notice returns the seed which is actually input to the VRF coordinator + * + * @dev To prevent repetition of VRF output due to repetition of the + * @dev user-supplied seed, that seed is combined in a hash with the + * @dev user-specific nonce, and the address of the consuming contract. The + * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in + * @dev the final seed, but the nonce does protect against repetition in + * @dev requests which are included in a single block. + * + * @param _userSeed VRF seed input provided by user + * @param _requester Address of the requesting contract + * @param _nonce User-specific nonce at the time of the request + */ + function makeVRFInputSeed( + bytes32 _keyHash, + uint256 _userSeed, + address _requester, + uint256 _nonce + ) + internal + pure + returns ( + uint256 + ) + { + return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); + } + + /** + * @notice Returns the id for this request + * @param _keyHash The serviceAgreement ID to be used for this request + * @param _vRFInputSeed The seed to be passed directly to the VRF + * @return The id for this request + * + * @dev Note that _vRFInputSeed is not the seed passed by the consuming + * @dev contract, but the one generated by makeVRFInputSeed + */ + function makeRequestId( + bytes32 _keyHash, + uint256 _vRFInputSeed + ) + internal + pure + returns ( + bytes32 + ) + { + return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ValidatorProxy.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ValidatorProxy.sol new file mode 100644 index 0000000..72f03fb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ValidatorProxy.sol @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./ConfirmedOwner.sol"; +import "../interfaces/AggregatorValidatorInterface.sol"; +import "../interfaces/TypeAndVersionInterface.sol"; + +contract ValidatorProxy is AggregatorValidatorInterface, TypeAndVersionInterface, ConfirmedOwner { + + /// @notice Uses a single storage slot to store the current address + struct AggregatorConfiguration { + address target; + bool hasNewProposal; + } + + struct ValidatorConfiguration { + AggregatorValidatorInterface target; + bool hasNewProposal; + } + + // Configuration for the current aggregator + AggregatorConfiguration private s_currentAggregator; + // Proposed aggregator address + address private s_proposedAggregator; + + // Configuration for the current validator + ValidatorConfiguration private s_currentValidator; + // Proposed validator address + AggregatorValidatorInterface private s_proposedValidator; + + event AggregatorProposed( + address indexed aggregator + ); + event AggregatorUpgraded( + address indexed previous, + address indexed current + ); + event ValidatorProposed( + AggregatorValidatorInterface indexed validator + ); + event ValidatorUpgraded( + AggregatorValidatorInterface indexed previous, + AggregatorValidatorInterface indexed current + ); + /// @notice The proposed aggregator called validate, but the call was not passed on to any validators + event ProposedAggregatorValidateCall( + address indexed proposed, + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ); + + /** + * @notice Construct the ValidatorProxy with an aggregator and a validator + * @param aggregator address + * @param validator address + */ + constructor( + address aggregator, + AggregatorValidatorInterface validator + ) + ConfirmedOwner(msg.sender) + { + s_currentAggregator = AggregatorConfiguration({ + target: aggregator, + hasNewProposal: false + }); + s_currentValidator = ValidatorConfiguration({ + target: validator, + hasNewProposal: false + }); + } + + /** + * @notice Validate a transmission + * @dev Must be called by either the `s_currentAggregator.target`, or the `s_proposedAggregator`. + * If called by the `s_currentAggregator.target` this function passes the call on to the `s_currentValidator.target` + * and the `s_proposedValidator`, if it is set. + * If called by the `s_proposedAggregator` this function emits a `ProposedAggregatorValidateCall` to signal that + * the call was received. + * @dev To guard against external `validate` calls reverting, we use raw calls here. + * We favour `call` over try-catch to ensure that failures are avoided even if the validator address is incorrectly + * set as a non-contract address. + * @dev If the `aggregator` and `validator` are the same contract or collude, this could exhibit reentrancy behavior. + * However, since that contract would have to be explicitly written for reentrancy and that the `owner` would have + * to configure this contract to use that malicious contract, we refrain from using mutex or check here. + * @dev This does not perform any checks on any roundId, so it is possible that a validator receive different reports + * for the same roundId at different points in time. Validator implementations should be aware of this. + * @param previousRoundId uint256 + * @param previousAnswer int256 + * @param currentRoundId uint256 + * @param currentAnswer int256 + * @return bool + */ + function validate( + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ) + external + override + returns ( + bool + ) + { + address currentAggregator = s_currentAggregator.target; + if (msg.sender != currentAggregator) { + address proposedAggregator = s_proposedAggregator; + require(msg.sender == proposedAggregator, "Not a configured aggregator"); + // If the aggregator is still in proposed state, emit an event and don't push to any validator. + // This is to confirm that `validate` is being called prior to upgrade. + emit ProposedAggregatorValidateCall( + proposedAggregator, + previousRoundId, + previousAnswer, + currentRoundId, + currentAnswer + ); + return true; + } + + // Send the validate call to the current validator + ValidatorConfiguration memory currentValidator = s_currentValidator; + address currentValidatorAddress = address(currentValidator.target); + require(currentValidatorAddress != address(0), "No validator set"); + currentValidatorAddress.call( + abi.encodeWithSelector( + AggregatorValidatorInterface.validate.selector, + previousRoundId, + previousAnswer, + currentRoundId, + currentAnswer + ) + ); + // If there is a new proposed validator, send the validate call to that validator also + if (currentValidator.hasNewProposal) { + address(s_proposedValidator).call( + abi.encodeWithSelector( + AggregatorValidatorInterface.validate.selector, + previousRoundId, + previousAnswer, + currentRoundId, + currentAnswer + ) + ); + } + return true; + } + + /** AGGREGATOR CONFIGURATION FUNCTIONS **/ + + /** + * @notice Propose an aggregator + * @dev A zero address can be used to unset the proposed aggregator. Only owner can call. + * @param proposed address + */ + function proposeNewAggregator( + address proposed + ) + external + onlyOwner() + { + require(s_proposedAggregator != proposed && s_currentAggregator.target != proposed, "Invalid proposal"); + s_proposedAggregator = proposed; + // If proposed is zero address, hasNewProposal = false + s_currentAggregator.hasNewProposal = (proposed != address(0)); + emit AggregatorProposed(proposed); + } + + /** + * @notice Upgrade the aggregator by setting the current aggregator as the proposed aggregator. + * @dev Must have a proposed aggregator. Only owner can call. + */ + function upgradeAggregator() + external + onlyOwner() + { + // Get configuration in memory + AggregatorConfiguration memory current = s_currentAggregator; + address previous = current.target; + address proposed = s_proposedAggregator; + + // Perform the upgrade + require(current.hasNewProposal, "No proposal"); + s_currentAggregator = AggregatorConfiguration({ + target: proposed, + hasNewProposal: false + }); + delete s_proposedAggregator; + + emit AggregatorUpgraded(previous, proposed); + } + + /** + * @notice Get aggregator details + * @return current address + * @return hasProposal bool + * @return proposed address + */ + function getAggregators() + external + view + returns( + address current, + bool hasProposal, + address proposed + ) + { + current = s_currentAggregator.target; + hasProposal = s_currentAggregator.hasNewProposal; + proposed = s_proposedAggregator; + } + + /** VALIDATOR CONFIGURATION FUNCTIONS **/ + + /** + * @notice Propose an validator + * @dev A zero address can be used to unset the proposed validator. Only owner can call. + * @param proposed address + */ + function proposeNewValidator( + AggregatorValidatorInterface proposed + ) + external + onlyOwner() + { + require(s_proposedValidator != proposed && s_currentValidator.target != proposed, "Invalid proposal"); + s_proposedValidator = proposed; + // If proposed is zero address, hasNewProposal = false + s_currentValidator.hasNewProposal = (address(proposed) != address(0)); + emit ValidatorProposed(proposed); + } + + /** + * @notice Upgrade the validator by setting the current validator as the proposed validator. + * @dev Must have a proposed validator. Only owner can call. + */ + function upgradeValidator() + external + onlyOwner() + { + // Get configuration in memory + ValidatorConfiguration memory current = s_currentValidator; + AggregatorValidatorInterface previous = current.target; + AggregatorValidatorInterface proposed = s_proposedValidator; + + // Perform the upgrade + require(current.hasNewProposal, "No proposal"); + s_currentValidator = ValidatorConfiguration({ + target: proposed, + hasNewProposal: false + }); + delete s_proposedValidator; + + emit ValidatorUpgraded(previous, proposed); + } + + /** + * @notice Get validator details + * @return current address + * @return hasProposal bool + * @return proposed address + */ + function getValidators() + external + view + returns( + AggregatorValidatorInterface current, + bool hasProposal, + AggregatorValidatorInterface proposed + ) + { + current = s_currentValidator.target; + hasProposal = s_currentValidator.hasNewProposal; + proposed = s_proposedValidator; + } + + /** + * @notice The type and version of this contract + * @return Type and version string + */ + function typeAndVersion() + external + pure + virtual + override + returns ( + string memory + ) + { + return "ValidatorProxy 1.0.0"; + } + +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/ArbitrumSequencerUptimeFeedInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/ArbitrumSequencerUptimeFeedInterface.sol new file mode 100644 index 0000000..6943b9f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/ArbitrumSequencerUptimeFeedInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ArbitrumSequencerUptimeFeedInterface { + function updateStatus(bool status, uint64 timestamp) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/CrossDomainOwnableInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/CrossDomainOwnableInterface.sol new file mode 100644 index 0000000..a4cc6a9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/CrossDomainOwnableInterface.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title CrossDomainOwnableInterface - A contract with helpers for cross-domain contract ownership +interface CrossDomainOwnableInterface { + event L1OwnershipTransferRequested(address indexed from, address indexed to); + + event L1OwnershipTransferred(address indexed from, address indexed to); + + function l1Owner() external returns (address); + + function transferL1Ownership(address recipient) external; + + function acceptL1Ownership() external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/DelegateForwarderInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/DelegateForwarderInterface.sol new file mode 100644 index 0000000..792e83e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/DelegateForwarderInterface.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title DelegateForwarderInterface - forwards a delegatecall to a target, under some conditions +interface DelegateForwarderInterface { + /** + * @notice forward delegatecalls the `target` with `data` + * @param target contract address to be delegatecalled + * @param data to send to target contract + */ + function forwardDelegate(address target, bytes memory data) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/FlagsInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/FlagsInterface.sol new file mode 100644 index 0000000..b5fd70b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/FlagsInterface.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +interface FlagsInterface { + function getFlag(address) external view returns (bool); + + function getFlags(address[] calldata) external view returns (bool[] memory); + + function raiseFlag(address) external; + + function raiseFlags(address[] calldata) external; + + function lowerFlag(address) external; + + function lowerFlags(address[] calldata) external; + + function setRaisingAccessController(address) external; + + function setLoweringAccessController(address) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/ForwarderInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/ForwarderInterface.sol new file mode 100644 index 0000000..a3c29e5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/ForwarderInterface.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title ForwarderInterface - forwards a call to a target, under some conditions +interface ForwarderInterface { + /** + * @notice forward calls the `target` with `data` + * @param target contract address to be called + * @param data to send to target contract + */ + function forward(address target, bytes memory data) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/IArbitrumDelayedInbox.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/IArbitrumDelayedInbox.sol new file mode 100644 index 0000000..cb2228d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/IArbitrumDelayedInbox.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol"; + +/** + * @notice This interface extends Arbitrum's IInbox interface to include + * the calculateRetryableSubmissionFee. This new function was added as part + * of Arbitrum's Nitro migration but was excluded from the IInbox interface. This setup + * works for us as the team has added it as a public function to the IInbox proxy + * contract's implementation + */ +interface IArbitrumDelayedInbox is IInbox { + function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/KeeperRegistryInterface1_3.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/KeeperRegistryInterface1_3.sol new file mode 100644 index 0000000..773440f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/KeeperRegistryInterface1_3.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @notice config of the registry + * @dev only used in params and return values + * @member paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @member blockCountPerTurn number of blocks each oracle has during their turn to + * perform upkeep before it will be the next keeper's turn to submit + * @member checkGasLimit gas limit when checking for upkeep + * @member stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling + * @member maxPerformGas max executeGas allowed for an upkeep on this registry + * @member fallbackGasPrice gas price used if the gas price feed is stale + * @member fallbackLinkPrice LINK price used if the LINK price feed is stale + * @member transcoder address of the transcoder contract + * @member registrar address of the registrar contract + */ +struct Config { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address registrar; +} + +/** + * @notice state of the registry + * @dev only used in params and return values + * @member nonce used for ID generation + * @member ownerLinkBalance withdrawable balance of LINK by contract owner + * @member expectedLinkBalance the expected balance of LINK of the registry + * @member numUpkeeps total number of upkeeps on the registry + */ +struct State { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint256 numUpkeeps; +} + +/** + * @notice relevant state of an upkeep + * @member balance the balance of this upkeep + * @member lastKeeper the keeper which last performs the upkeep + * @member executeGas the gas limit of upkeep execution + * @member maxValidBlocknumber until which block this upkeep is valid + * @member target the contract which needs to be serviced + * @member amountSpent the amount this upkeep has spent + * @member admin the upkeep admin + * @member paused if this upkeep has been paused + */ +struct Upkeep { + uint96 balance; + address lastKeeper; // 1 full evm word + uint96 amountSpent; + address admin; // 2 full evm words + uint32 executeGas; + uint32 maxValidBlocknumber; + address target; + bool paused; // 24 bits to 3 full evm words +} + +interface KeeperRegistryBaseInterface { + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id); + + function performUpkeep(uint256 id, bytes calldata performData) external returns (bool success); + + function cancelUpkeep(uint256 id) external; + + function pauseUpkeep(uint256 id) external; + + function unpauseUpkeep(uint256 id) external; + + function transferUpkeepAdmin(uint256 id, address proposed) external; + + function acceptUpkeepAdmin(uint256 id) external; + + function updateCheckData(uint256 id, bytes calldata newCheckData) external; + + function addFunds(uint256 id, uint96 amount) external; + + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; + + function getUpkeep(uint256 id) + external + view + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent, + bool paused + ); + + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); + + function getKeeperInfo(address query) + external + view + returns ( + address payee, + bool active, + uint96 balance + ); + + function getState() + external + view + returns ( + State memory, + Config memory, + address[] memory + ); +} + +/** + * @dev The view methods are not actually marked as view in the implementation + * but we want them to be easily queried off-chain. Solidity will not compile + * if we actually inherit from this interface, so we document it here. + */ +interface KeeperRegistryInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + view + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + int256 gasWei, + int256 linkEth + ); +} + +interface KeeperRegistryExecutableInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/OptimismSequencerUptimeFeedInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/OptimismSequencerUptimeFeedInterface.sol new file mode 100644 index 0000000..281966b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/interfaces/OptimismSequencerUptimeFeedInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface OptimismSequencerUptimeFeedInterface { + function updateStatus(bool status, uint64 timestamp) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistrar2_0.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistrar2_0.sol new file mode 100644 index 0000000..5b62c93 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistrar2_0.sol @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "../../interfaces/LinkTokenInterface.sol"; +import "./interfaces/KeeperRegistryInterface2_0.sol"; +import "../../interfaces/TypeAndVersionInterface.sol"; +import "../../ConfirmedOwner.sol"; +import "../../interfaces/ERC677ReceiverInterface.sol"; + +/** + * @notice Contract to accept requests for upkeep registrations + * @dev There are 2 registration workflows in this contract + * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually + * calls `approve` to register upkeep and emit events to inform UI and others interested. + * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on + * keeper registry and then emits approved event to finish the flow automatically without manual intervention. + * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not. + * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations. + */ +contract KeeperRegistrar2_0 is TypeAndVersionInterface, ConfirmedOwner, ERC677ReceiverInterface { + /** + * DISABLED: No auto approvals, all new upkeeps should be approved manually. + * ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest. + * ENABLED_ALL: Auto approvals for all new upkeeps subject to max allowed. + */ + enum AutoApproveType { + DISABLED, + ENABLED_SENDER_ALLOWLIST, + ENABLED_ALL + } + + bytes4 private constant REGISTER_REQUEST_SELECTOR = this.register.selector; + + mapping(bytes32 => PendingRequest) private s_pendingRequests; + + LinkTokenInterface public immutable LINK; + + /** + * @notice versions: + * - KeeperRegistrar 2.0.0: Remove source from register + * Breaks our example of "Register an Upkeep using your own deployed contract" + * - KeeperRegistrar 1.1.0: Add functionality for sender allowlist in auto approve + * : Remove rate limit and add max allowed for auto approve + * - KeeperRegistrar 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistrar 2.0.0"; + + struct RegistrarConfig { + AutoApproveType autoApproveConfigType; + uint32 autoApproveMaxAllowed; + uint32 approvedCount; + KeeperRegistryBaseInterface keeperRegistry; + uint96 minLINKJuels; + } + + struct PendingRequest { + address admin; + uint96 balance; + } + + RegistrarConfig private s_config; + // Only applicable if s_config.configType is ENABLED_SENDER_ALLOWLIST + mapping(address => bool) private s_autoApproveAllowedSenders; + + event RegistrationRequested( + bytes32 indexed hash, + string name, + bytes encryptedEmail, + address indexed upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes checkData, + uint96 amount + ); + + event RegistrationApproved(bytes32 indexed hash, string displayName, uint256 indexed upkeepId); + + event RegistrationRejected(bytes32 indexed hash); + + event AutoApproveAllowedSenderSet(address indexed senderAddress, bool allowed); + + event ConfigChanged( + AutoApproveType autoApproveConfigType, + uint32 autoApproveMaxAllowed, + address keeperRegistry, + uint96 minLINKJuels + ); + + error InvalidAdminAddress(); + error RequestNotFound(); + error HashMismatch(); + error OnlyAdminOrOwner(); + error InsufficientPayment(); + error RegistrationRequestFailed(); + error OnlyLink(); + error AmountMismatch(); + error SenderMismatch(); + error FunctionNotPermitted(); + error LinkTransferFailed(address to); + error InvalidDataLength(); + + /* + * @param LINKAddress Address of Link token + * @param autoApproveConfigType setting for auto-approve registrations + * @param autoApproveMaxAllowed max number of registrations that can be auto approved + * @param keeperRegistry keeper registry address + * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with + */ + constructor( + address LINKAddress, + AutoApproveType autoApproveConfigType, + uint16 autoApproveMaxAllowed, + address keeperRegistry, + uint96 minLINKJuels + ) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(LINKAddress); + setRegistrationConfig(autoApproveConfigType, autoApproveMaxAllowed, keeperRegistry, minLINKJuels); + } + + //EXTERNAL + + /** + * @notice register can only be called through transferAndCall on LINK contract + * @param name string of the upkeep to be registered + * @param encryptedEmail email address of upkeep contact + * @param upkeepContract address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when performing upkeep + * @param adminAddress address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + * @param amount quantity of LINK upkeep is funded with (specified in Juels) + * @param sender address of the sender making the request + */ + function register( + string memory name, + bytes calldata encryptedEmail, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + address sender + ) external onlyLINK { + _register(name, encryptedEmail, upkeepContract, gasLimit, adminAddress, checkData, amount, sender); + } + + /** + * @notice Allows external users to register upkeeps; assumes amount is approved for transfer by the contract + * @param name string of the upkeep to be registered + * @param encryptedEmail email address of upkeep contact + * @param upkeepContract address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when performing upkeep + * @param adminAddress address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + * @param amount quantity of LINK upkeep is funded with (specified in Juels) + */ + function registerUpkeep( + string memory name, + bytes calldata encryptedEmail, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount + ) external returns (uint256) { + if (amount < s_config.minLINKJuels) { + revert InsufficientPayment(); + } + + LINK.transferFrom(msg.sender, address(this), amount); + return _register(name, encryptedEmail, upkeepContract, gasLimit, adminAddress, checkData, amount, msg.sender); + } + + /** + * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event + */ + function approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + bytes32 hash + ) external onlyOwner { + PendingRequest memory request = s_pendingRequests[hash]; + if (request.admin == address(0)) { + revert RequestNotFound(); + } + bytes32 expectedHash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); + if (hash != expectedHash) { + revert HashMismatch(); + } + delete s_pendingRequests[hash]; + _approve(name, upkeepContract, gasLimit, adminAddress, checkData, request.balance, hash); + } + + /** + * @notice cancel will remove a registration request and return the refunds to the msg.sender + * @param hash the request hash + */ + function cancel(bytes32 hash) external { + PendingRequest memory request = s_pendingRequests[hash]; + if (!(msg.sender == request.admin || msg.sender == owner())) { + revert OnlyAdminOrOwner(); + } + if (request.admin == address(0)) { + revert RequestNotFound(); + } + delete s_pendingRequests[hash]; + bool success = LINK.transfer(msg.sender, request.balance); + if (!success) { + revert LinkTransferFailed(msg.sender); + } + emit RegistrationRejected(hash); + } + + /** + * @notice owner calls this function to set if registration requests should be sent directly to the Keeper Registry + * @param autoApproveConfigType setting for auto-approve registrations + * note: autoApproveAllowedSenders list persists across config changes irrespective of type + * @param autoApproveMaxAllowed max number of registrations that can be auto approved + * @param keeperRegistry new keeper registry address + * @param minLINKJuels minimum LINK that new registrations should fund their upkeep with + */ + function setRegistrationConfig( + AutoApproveType autoApproveConfigType, + uint16 autoApproveMaxAllowed, + address keeperRegistry, + uint96 minLINKJuels + ) public onlyOwner { + uint32 approvedCount = s_config.approvedCount; + s_config = RegistrarConfig({ + autoApproveConfigType: autoApproveConfigType, + autoApproveMaxAllowed: autoApproveMaxAllowed, + approvedCount: approvedCount, + minLINKJuels: minLINKJuels, + keeperRegistry: KeeperRegistryBaseInterface(keeperRegistry) + }); + + emit ConfigChanged(autoApproveConfigType, autoApproveMaxAllowed, keeperRegistry, minLINKJuels); + } + + /** + * @notice owner calls this function to set allowlist status for senderAddress + * @param senderAddress senderAddress to set the allowlist status for + * @param allowed true if senderAddress needs to be added to allowlist, false if needs to be removed + */ + function setAutoApproveAllowedSender(address senderAddress, bool allowed) external onlyOwner { + s_autoApproveAllowedSenders[senderAddress] = allowed; + + emit AutoApproveAllowedSenderSet(senderAddress, allowed); + } + + /** + * @notice read the allowlist status of senderAddress + * @param senderAddress address to read the allowlist status for + */ + function getAutoApproveAllowedSender(address senderAddress) external view returns (bool) { + return s_autoApproveAllowedSenders[senderAddress]; + } + + /** + * @notice read the current registration configuration + */ + function getRegistrationConfig() + external + view + returns ( + AutoApproveType autoApproveConfigType, + uint32 autoApproveMaxAllowed, + uint32 approvedCount, + address keeperRegistry, + uint256 minLINKJuels + ) + { + RegistrarConfig memory config = s_config; + return ( + config.autoApproveConfigType, + config.autoApproveMaxAllowed, + config.approvedCount, + address(config.keeperRegistry), + config.minLINKJuels + ); + } + + /** + * @notice gets the admin address and the current balance of a registration request + */ + function getPendingRequest(bytes32 hash) external view returns (address, uint96) { + PendingRequest memory request = s_pendingRequests[hash]; + return (request.admin, request.balance); + } + + /** + * @notice Called when LINK is sent to the contract via `transferAndCall` + * @param sender Address of the sender transfering LINK + * @param amount Amount of LINK sent (specified in Juels) + * @param data Payload of the transaction + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) + external + override + onlyLINK + permittedFunctionsForLINK(data) + isActualAmount(amount, data) + isActualSender(sender, data) + { + if (data.length < 292) revert InvalidDataLength(); + if (amount < s_config.minLINKJuels) { + revert InsufficientPayment(); + } + (bool success, ) = address(this).delegatecall(data); + // calls register + if (!success) { + revert RegistrationRequestFailed(); + } + } + + //PRIVATE + + /** + * @dev verify registration request and emit RegistrationRequested event + */ + function _register( + string memory name, + bytes calldata encryptedEmail, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + address sender + ) private returns (uint256) { + if (adminAddress == address(0)) { + revert InvalidAdminAddress(); + } + bytes32 hash = keccak256(abi.encode(upkeepContract, gasLimit, adminAddress, checkData)); + + emit RegistrationRequested(hash, name, encryptedEmail, upkeepContract, gasLimit, adminAddress, checkData, amount); + + uint256 upkeepId; + RegistrarConfig memory config = s_config; + if (_shouldAutoApprove(config, sender)) { + s_config.approvedCount = config.approvedCount + 1; + + upkeepId = _approve(name, upkeepContract, gasLimit, adminAddress, checkData, amount, hash); + } else { + uint96 newBalance = s_pendingRequests[hash].balance + amount; + s_pendingRequests[hash] = PendingRequest({admin: adminAddress, balance: newBalance}); + } + + return upkeepId; + } + + /** + * @dev register upkeep on KeeperRegistry contract and emit RegistrationApproved event + */ + function _approve( + string memory name, + address upkeepContract, + uint32 gasLimit, + address adminAddress, + bytes calldata checkData, + uint96 amount, + bytes32 hash + ) private returns (uint256) { + KeeperRegistryBaseInterface keeperRegistry = s_config.keeperRegistry; + + // register upkeep + uint256 upkeepId = keeperRegistry.registerUpkeep(upkeepContract, gasLimit, adminAddress, checkData); + // fund upkeep + bool success = LINK.transferAndCall(address(keeperRegistry), amount, abi.encode(upkeepId)); + if (!success) { + revert LinkTransferFailed(address(keeperRegistry)); + } + + emit RegistrationApproved(hash, name, upkeepId); + + return upkeepId; + } + + /** + * @dev verify sender allowlist if needed and check max limit + */ + function _shouldAutoApprove(RegistrarConfig memory config, address sender) private view returns (bool) { + if (config.autoApproveConfigType == AutoApproveType.DISABLED) { + return false; + } + if ( + config.autoApproveConfigType == AutoApproveType.ENABLED_SENDER_ALLOWLIST && (!s_autoApproveAllowedSenders[sender]) + ) { + return false; + } + if (config.approvedCount < config.autoApproveMaxAllowed) { + return true; + } + return false; + } + + //MODIFIERS + + /** + * @dev Reverts if not sent from the LINK token + */ + modifier onlyLINK() { + if (msg.sender != address(LINK)) { + revert OnlyLink(); + } + _; + } + + /** + * @dev Reverts if the given data does not begin with the `register` function selector + * @param _data The data payload of the request + */ + modifier permittedFunctionsForLINK(bytes memory _data) { + bytes4 funcSelector; + assembly { + // solhint-disable-next-line avoid-low-level-calls + funcSelector := mload(add(_data, 32)) // First 32 bytes contain length of data + } + if (funcSelector != REGISTER_REQUEST_SELECTOR) { + revert FunctionNotPermitted(); + } + _; + } + + /** + * @dev Reverts if the actual amount passed does not match the expected amount + * @param expected amount that should match the actual amount + * @param data bytes + */ + modifier isActualAmount(uint256 expected, bytes calldata data) { + // decode register function arguments to get actual amount + (, , , , , , uint96 amount, ) = abi.decode( + data[4:], + (string, bytes, address, uint32, address, bytes, uint96, address) + ); + if (expected != amount) { + revert AmountMismatch(); + } + _; + } + + /** + * @dev Reverts if the actual sender address does not match the expected sender address + * @param expected address that should match the actual sender address + * @param data bytes + */ + modifier isActualSender(address expected, bytes calldata data) { + // decode register function arguments to get actual sender + (, , , , , , , address sender) = abi.decode( + data[4:], + (string, bytes, address, uint32, address, bytes, uint96, address) + ); + if (expected != sender) { + revert SenderMismatch(); + } + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistry2_0.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistry2_0.sol new file mode 100644 index 0000000..abc897d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistry2_0.sol @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/proxy/Proxy.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "./KeeperRegistryBase2_0.sol"; +import {KeeperRegistryExecutableInterface} from "./interfaces/KeeperRegistryInterface2_0.sol"; +import "../../interfaces/MigratableKeeperRegistryInterface.sol"; +import "../../interfaces/TypeAndVersionInterface.sol"; +import "../../interfaces/ERC677ReceiverInterface.sol"; + +/** + * @notice Registry for adding work for Chainlink Keepers to perform on client + * contracts. Clients must support the Upkeep interface. + */ +contract KeeperRegistry2_0 is + KeeperRegistryBase2_0, + Proxy, + TypeAndVersionInterface, + KeeperRegistryExecutableInterface, + MigratableKeeperRegistryInterface, + ERC677ReceiverInterface +{ + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + + address public immutable KEEPER_REGISTRY_LOGIC; + + /** + * @notice versions: + * - KeeperRegistry 1.3.0: split contract into Proxy and Logic + * : account for Arbitrum and Optimism L1 gas fee + * : allow users to configure upkeeps + * - KeeperRegistry 1.2.0: allow funding within performUpkeep + * : allow configurable registry maxPerformGas + * : add function to let admin change upkeep gas limit + * : add minUpkeepSpend requirement + * : upgrade to solidity v0.8 + * - KeeperRegistry 1.1.0: added flatFeeMicroLink + * - KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "KeeperRegistry 1.3.0"; + + /** + * @param paymentModel one of Default, Arbitrum, and Optimism + * @param registryGasOverhead the gas overhead used by registry in performUpkeep + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + * @param config registry config settings + */ + constructor( + PaymentModel paymentModel, + uint256 registryGasOverhead, + address link, + address linkEthFeed, + address fastGasFeed, + address keeperRegistryLogic, + Config memory config + ) KeeperRegistryBase2_0(paymentModel, registryGasOverhead, link, linkEthFeed, fastGasFeed) { + KEEPER_REGISTRY_LOGIC = keeperRegistryLogic; + setConfig(config); + } + + // ACTIONS + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external override returns (uint256 id) { + // Executed through logic contract + _fallback(); + } + + /** + * @notice simulated by keepers via eth_call to see if the upkeep needs to be + * performed. If upkeep is needed, the call then simulates performUpkeep + * to make sure it succeeds. Finally, it returns the success status along with + * payment information and the perform data payload. + * @param id identifier of the upkeep to check + * @param from the address to simulate performing the upkeep from + */ + function checkUpkeep(uint256 id, address from) + external + override + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + // Executed through logic contract + _fallback(); + } + + /** + * @notice executes the upkeep with the perform data returned from + * checkUpkeep, validates the keeper's permissions, and pays the keeper. + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + */ + function performUpkeep(uint256 id, bytes calldata performData) + external + override + whenNotPaused + returns (bool success) + { + return _performUpkeepWithParams(_generatePerformParams(msg.sender, id, performData, true)); + } + + /** + * @notice prevent an upkeep from being performed in the future + * @param id upkeep to be canceled + */ + function cancelUpkeep(uint256 id) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice pause an upkeep + * @param id upkeep to be paused + */ + function pauseUpkeep(uint256 id) external override { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + if (upkeep.paused) revert OnlyUnpausedUpkeep(); + s_upkeep[id].paused = true; + s_upkeepIDs.remove(id); + emit UpkeepPaused(id); + } + + /** + * @notice unpause an upkeep + * @param id upkeep to be resumed + */ + function unpauseUpkeep(uint256 id) external override { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + if (!upkeep.paused) revert OnlyPausedUpkeep(); + s_upkeep[id].paused = false; + s_upkeepIDs.add(id); + emit UpkeepUnpaused(id); + } + + /** + * @notice update the check data of an upkeep + * @param id the id of the upkeep whose check data needs to be updated + * @param newCheckData the new check data + */ + function updateCheckData(uint256 id, bytes calldata newCheckData) external override { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + s_checkData[id] = newCheckData; + emit UpkeepCheckDataUpdated(id, newCheckData); + } + + /** + * @notice adds LINK funding for an upkeep by transferring from the sender's + * LINK balance + * @param id upkeep to fund + * @param amount number of LINK to transfer + */ + function addFunds(uint256 id, uint96 amount) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external override { + if (msg.sender != address(LINK)) revert OnlyCallableByLINKToken(); + if (data.length != 32) revert InvalidDataLength(); + uint256 id = abi.decode(data, (uint256)); + if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + + s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); + s_expectedLinkBalance = s_expectedLinkBalance + amount; + + emit FundsAdded(id, sender, uint96(amount)); + } + + /** + * @notice removes funding from a canceled upkeep + * @param id upkeep to withdraw funds from + * @param to destination address for sending remaining funds + */ + function withdrawFunds(uint256 id, address to) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice withdraws LINK funds collected through cancellation fees + */ + function withdrawOwnerFunds() external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice allows the admin of an upkeep to modify gas limit + * @param id upkeep to be change the gas limit for + * @param gasLimit new gas limit for the upkeep + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice recovers LINK funds improperly transferred to the registry + * @dev In principle this function’s execution cost could exceed block + * gas limit. However, in our anticipated deployment, the number of upkeeps and + * keepers will be low enough to avoid this problem. + */ + function recoverFunds() external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice withdraws a keeper's payment, callable only by the keeper's payee + * @param from keeper address + * @param to address to send the payment to + */ + function withdrawPayment(address from, address to) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice proposes the safe transfer of a keeper's payee to another address + * @param keeper address of the keeper to transfer payee role + * @param proposed address to nominate for next payeeship + */ + function transferPayeeship(address keeper, address proposed) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice accepts the safe transfer of payee role for a keeper + * @param keeper address to accept the payee role for + */ + function acceptPayeeship(address keeper) external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice proposes the safe transfer of an upkeep's admin role to another address + * @param id the upkeep id to transfer admin + * @param proposed address to nominate for the new upkeep admin + */ + function transferUpkeepAdmin(uint256 id, address proposed) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice accepts the safe transfer of admin role for an upkeep + * @param id the upkeep id + */ + function acceptUpkeepAdmin(uint256 id) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @notice signals to keepers that they should not perform upkeeps until the + * contract has been unpaused + */ + function pause() external { + // Executed through logic contract + _fallback(); + } + + /** + * @notice signals to keepers that they can perform upkeeps once again after + * having been paused + */ + function unpause() external { + // Executed through logic contract + _fallback(); + } + + // SETTERS + + /** + * @notice updates the configuration of the registry + * @param config registry config fields + */ + function setConfig(Config memory config) public onlyOwner { + if (config.maxPerformGas < s_storage.maxPerformGas) revert GasLimitCanOnlyIncrease(); + s_storage = Storage({ + paymentPremiumPPB: config.paymentPremiumPPB, + flatFeeMicroLink: config.flatFeeMicroLink, + blockCountPerTurn: config.blockCountPerTurn, + checkGasLimit: config.checkGasLimit, + stalenessSeconds: config.stalenessSeconds, + gasCeilingMultiplier: config.gasCeilingMultiplier, + minUpkeepSpend: config.minUpkeepSpend, + maxPerformGas: config.maxPerformGas, + nonce: s_storage.nonce + }); + s_fallbackGasPrice = config.fallbackGasPrice; + s_fallbackLinkPrice = config.fallbackLinkPrice; + s_transcoder = config.transcoder; + s_registrar = config.registrar; + emit ConfigSet(config); + } + + /** + * @notice update the list of keepers allowed to perform upkeep + * @param keepers list of addresses allowed to perform upkeep + * @param payees addresses corresponding to keepers who are allowed to + * move payments which have been accrued + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external { + // Executed through logic contract + _fallback(); + } + + // GETTERS + + /** + * @notice read all of the details about an upkeep + */ + function getUpkeep(uint256 id) + external + view + override + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent, + bool paused + ) + { + Upkeep memory reg = s_upkeep[id]; + return ( + reg.target, + reg.executeGas, + s_checkData[id], + reg.balance, + reg.lastKeeper, + reg.admin, + reg.maxValidBlocknumber, + reg.amountSpent, + reg.paused + ); + } + + /** + * @notice retrieve active upkeep IDs. Active upkeep is defined as an upkeep which is not paused and not canceled. + * @param startIndex starting index in list + * @param maxCount max count to retrieve (0 = unlimited) + * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one + * should consider keeping the blockheight constant to ensure a holistic picture of the contract state + */ + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view override returns (uint256[] memory) { + uint256 maxIdx = s_upkeepIDs.length(); + if (startIndex >= maxIdx) revert IndexOutOfRange(); + if (maxCount == 0) { + maxCount = maxIdx - startIndex; + } + uint256[] memory ids = new uint256[](maxCount); + for (uint256 idx = 0; idx < maxCount; idx++) { + ids[idx] = s_upkeepIDs.at(startIndex + idx); + } + return ids; + } + + /** + * @notice read the current info about any keeper address + */ + function getKeeperInfo(address query) + external + view + override + returns ( + address payee, + bool active, + uint96 balance + ) + { + KeeperInfo memory keeper = s_keeperInfo[query]; + return (keeper.payee, keeper.active, keeper.balance); + } + + /** + * @notice read the current state of the registry + */ + function getState() + external + view + override + returns ( + State memory state, + Config memory config, + address[] memory keepers + ) + { + Storage memory store = s_storage; + state.nonce = store.nonce; + state.ownerLinkBalance = s_ownerLinkBalance; + state.expectedLinkBalance = s_expectedLinkBalance; + state.numUpkeeps = s_upkeepIDs.length(); + config.paymentPremiumPPB = store.paymentPremiumPPB; + config.flatFeeMicroLink = store.flatFeeMicroLink; + config.blockCountPerTurn = store.blockCountPerTurn; + config.checkGasLimit = store.checkGasLimit; + config.stalenessSeconds = store.stalenessSeconds; + config.gasCeilingMultiplier = store.gasCeilingMultiplier; + config.minUpkeepSpend = store.minUpkeepSpend; + config.maxPerformGas = store.maxPerformGas; + config.fallbackGasPrice = s_fallbackGasPrice; + config.fallbackLinkPrice = s_fallbackLinkPrice; + config.transcoder = s_transcoder; + config.registrar = s_registrar; + return (state, config, s_keeperList); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance) { + return getMaxPaymentForGas(s_upkeep[id].executeGas); + } + + /** + * @notice calculates the maximum payment for a given gas limit + * @param gasLimit the gas to calculate payment for + */ + function getMaxPaymentForGas(uint256 gasLimit) public view returns (uint96 maxPayment) { + (uint256 fastGasWei, uint256 linkEth) = _getFeedData(); + return _calculatePaymentAmount(gasLimit, fastGasWei, linkEth, false); + } + + /** + * @notice retrieves the migration permission for a peer registry + */ + function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { + return s_peerRegistryMigrationPermission[peer]; + } + + /** + * @notice sets the peer registry migration permission + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external { + // Executed through logic contract + _fallback(); + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + UpkeepFormat public constant override upkeepTranscoderVersion = UPKEEP_TRANSCODER_VERSION_BASE; + + /** + * @inheritdoc MigratableKeeperRegistryInterface + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external override { + // Executed through logic contract + _fallback(); + } + + /** + * @dev This is the address to which proxy functions are delegated to + */ + function _implementation() internal view override returns (address) { + return KEEPER_REGISTRY_LOGIC; + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available + */ + function _callWithExactGas( + uint256 gasAmount, + address target, + bytes memory data + ) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= PERFORM_GAS_CUSHION and check for underflow + if lt(g, PERFORM_GAS_CUSHION) { + revert(0, 0) + } + g := sub(g, PERFORM_GAS_CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * keeper and the exact gas required by the Upkeep + */ + function _performUpkeepWithParams(PerformParams memory params) private nonReentrant returns (bool success) { + Upkeep memory upkeep = s_upkeep[params.id]; + if (upkeep.maxValidBlocknumber <= block.number) revert UpkeepCancelled(); + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + uint256 gasUsed = gasleft(); + bytes memory callData = abi.encodeWithSelector(PERFORM_SELECTOR, params.performData); + success = _callWithExactGas(params.gasLimit, upkeep.target, callData); + gasUsed = gasUsed - gasleft(); + uint96 payment = _calculatePaymentAmount(gasUsed, params.fastGasWei, params.linkEth, true); + + s_upkeep[params.id].balance = s_upkeep[params.id].balance - payment; + s_upkeep[params.id].amountSpent = s_upkeep[params.id].amountSpent + payment; + s_upkeep[params.id].lastKeeper = params.from; + s_keeperInfo[params.from].balance = s_keeperInfo[params.from].balance + payment; + + emit UpkeepPerformed(params.id, success, params.from, payment, params.performData); + return success; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistryBase2_0.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistryBase2_0.sol new file mode 100644 index 0000000..e78060d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistryBase2_0.sol @@ -0,0 +1,304 @@ +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/security/Pausable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import "../vendor/@eth-optimism/contracts/0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; +import "../ExecutionPrevention.sol"; +import {Config, State, Upkeep} from "./interfaces/KeeperRegistryInterface2_0.sol"; +import "../../ConfirmedOwner.sol"; +import "../../interfaces/AggregatorV3Interface.sol"; +import "../../interfaces/LinkTokenInterface.sol"; +import "../../interfaces/KeeperCompatibleInterface.sol"; +import "../../interfaces/UpkeepTranscoderInterface.sol"; + +/** + * @notice Base Keeper Registry contract, contains shared logic between + * KeeperRegistry and KeeperRegistryLogic + */ +abstract contract KeeperRegistryBase2_0 is ConfirmedOwner, ExecutionPrevention, ReentrancyGuard, Pausable { + address internal constant ZERO_ADDRESS = address(0); + address internal constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 internal constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 internal constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + uint256 internal constant PERFORM_GAS_MIN = 2_300; + uint256 internal constant CANCELLATION_DELAY = 50; + uint256 internal constant PERFORM_GAS_CUSHION = 5_000; + uint256 internal constant PPB_BASE = 1_000_000_000; + uint32 internal constant UINT32_MAX = type(uint32).max; + uint96 internal constant LINK_TOTAL_SUPPLY = 1e27; + UpkeepFormat internal constant UPKEEP_TRANSCODER_VERSION_BASE = UpkeepFormat.V2; + + // L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism + bytes public L1_FEE_DATA_PADDING = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + // MAX_INPUT_DATA represents the estimated max size of the sum of L1 data padding and msg.data in performUpkeep + // function, which includes 4 bytes for function selector, 32 bytes for upkeep id, 35 bytes for data padding, and + // 64 bytes for estimated perform data + bytes public MAX_INPUT_DATA = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + + address[] internal s_keeperList; + EnumerableSet.UintSet internal s_upkeepIDs; + mapping(uint256 => Upkeep) internal s_upkeep; + mapping(address => KeeperInfo) internal s_keeperInfo; + mapping(address => address) internal s_proposedPayee; + mapping(uint256 => address) internal s_proposedAdmin; + mapping(uint256 => bytes) internal s_checkData; + mapping(address => MigrationPermission) internal s_peerRegistryMigrationPermission; + Storage internal s_storage; + uint256 internal s_fallbackGasPrice; // not in config object for gas savings + uint256 internal s_fallbackLinkPrice; // not in config object for gas savings + uint96 internal s_ownerLinkBalance; + uint256 internal s_expectedLinkBalance; + address internal s_transcoder; + address internal s_registrar; + + LinkTokenInterface public immutable LINK; + AggregatorV3Interface public immutable LINK_ETH_FEED; + AggregatorV3Interface public immutable FAST_GAS_FEED; + OVM_GasPriceOracle public immutable OPTIMISM_ORACLE = OVM_GasPriceOracle(0x420000000000000000000000000000000000000F); + ArbGasInfo public immutable ARB_NITRO_ORACLE = ArbGasInfo(0x000000000000000000000000000000000000006C); + PaymentModel public immutable PAYMENT_MODEL; + uint256 public immutable REGISTRY_GAS_OVERHEAD; + + error ArrayHasNoEntries(); + error CannotCancel(); + error DuplicateEntry(); + error GasLimitCanOnlyIncrease(); + error GasLimitOutsideRange(); + error IndexOutOfRange(); + error InsufficientFunds(); + error InvalidDataLength(); + error InvalidPayee(); + error InvalidRecipient(); + error KeepersMustTakeTurns(); + error MigrationNotPermitted(); + error NotAContract(); + error OnlyActiveKeepers(); + error OnlyCallableByAdmin(); + error OnlyCallableByLINKToken(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedAdmin(); + error OnlyCallableByProposedPayee(); + error OnlyPausedUpkeep(); + error OnlyUnpausedUpkeep(); + error ParameterLengthError(); + error PaymentGreaterThanAllLINK(); + error TargetCheckReverted(bytes reason); + error TranscoderNotSet(); + error UpkeepCancelled(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error ValueNotChanged(); + + enum MigrationPermission { + NONE, + OUTGOING, + INCOMING, + BIDIRECTIONAL + } + + enum PaymentModel { + DEFAULT, + ARBITRUM, + OPTIMISM + } + + /** + * @notice storage of the registry, contains a mix of config and state data + */ + struct Storage { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; // 1 full evm word + uint32 maxPerformGas; + uint32 nonce; + } + + struct KeeperInfo { + address payee; + uint96 balance; + bool active; + } + + struct PerformParams { + address from; + uint256 id; + bytes performData; + uint256 maxLinkPayment; + uint256 gasLimit; + uint256 fastGasWei; + uint256 linkEth; + } + + event ConfigSet(Config config); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event KeepersUpdated(address[] keepers, address[] payees); + event OwnerFundsWithdrawn(uint96 amount); + event PayeeshipTransferRequested(address indexed keeper, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed keeper, address indexed from, address indexed to); + event PaymentWithdrawn(address indexed keeper, uint256 indexed amount, address indexed to, address payee); + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event UpkeepCheckDataUpdated(uint256 indexed id, bytes newCheckData); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepPaused(uint256 indexed id); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + address indexed from, + uint96 payment, + bytes performData + ); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event UpkeepUnpaused(uint256 indexed id); + event UpkeepRegistered(uint256 indexed id, uint32 executeGas, address admin); + + /** + * @param paymentModel the payment model of default, Arbitrum, or Optimism + * @param registryGasOverhead the gas overhead used by registry in performUpkeep + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + */ + constructor( + PaymentModel paymentModel, + uint256 registryGasOverhead, + address link, + address linkEthFeed, + address fastGasFeed + ) ConfirmedOwner(msg.sender) { + PAYMENT_MODEL = paymentModel; + REGISTRY_GAS_OVERHEAD = registryGasOverhead; + LINK = LinkTokenInterface(link); + LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); + FAST_GAS_FEED = AggregatorV3Interface(fastGasFeed); + } + + /** + * @dev retrieves feed data for fast gas/eth and link/eth prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function _getFeedData() internal view returns (uint256 gasWei, uint256 linkEth) { + uint32 stalenessSeconds = s_storage.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = FAST_GAS_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = LINK_ETH_FEED.latestRoundData(); + if ((staleFallback && stalenessSeconds < block.timestamp - timestamp) || feedValue <= 0) { + linkEth = s_fallbackLinkPrice; + } else { + linkEth = uint256(feedValue); + } + return (gasWei, linkEth); + } + + /** + * @dev calculates LINK paid for gas spent plus a configure premium percentage + * @param gasLimit the amount of gas used + * @param fastGasWei the fast gas price + * @param linkEth the exchange ratio between LINK and ETH + * @param isExecution if this is triggered by a perform upkeep function + */ + function _calculatePaymentAmount( + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkEth, + bool isExecution + ) internal view returns (uint96 payment) { + Storage memory store = s_storage; + uint256 gasWei = fastGasWei * store.gasCeilingMultiplier; + // in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier + if (isExecution && tx.gasprice < gasWei) { + gasWei = tx.gasprice; + } + + uint256 weiForGas = gasWei * (gasLimit + REGISTRY_GAS_OVERHEAD); + uint256 premium = PPB_BASE + store.paymentPremiumPPB; + uint256 l1CostWei = 0; + if (PAYMENT_MODEL == PaymentModel.OPTIMISM) { + bytes memory txCallData = new bytes(0); + if (isExecution) { + txCallData = bytes.concat(msg.data, L1_FEE_DATA_PADDING); + } else { + txCallData = MAX_INPUT_DATA; + } + l1CostWei = OPTIMISM_ORACLE.getL1Fee(txCallData); + } else if (PAYMENT_MODEL == PaymentModel.ARBITRUM) { + l1CostWei = ARB_NITRO_ORACLE.getCurrentTxL1GasFees(); + } + // if it's not performing upkeeps, use gas ceiling multiplier to estimate the upper bound + if (!isExecution) { + l1CostWei = store.gasCeilingMultiplier * l1CostWei; + } + + uint256 total = ((weiForGas + l1CostWei) * 1e9 * premium) / linkEth + uint256(store.flatFeeMicroLink) * 1e12; + if (total > LINK_TOTAL_SUPPLY) revert PaymentGreaterThanAllLINK(); + return uint96(total); // LINK_TOTAL_SUPPLY < UINT96_MAX + } + + /** + * @dev ensures all required checks are passed before an upkeep is performed + */ + function _prePerformUpkeep( + Upkeep memory upkeep, + address from, + uint256 maxLinkPayment + ) internal view { + if (upkeep.paused) revert OnlyUnpausedUpkeep(); + if (!s_keeperInfo[from].active) revert OnlyActiveKeepers(); + if (upkeep.balance < maxLinkPayment) revert InsufficientFunds(); + if (upkeep.lastKeeper == from) revert KeepersMustTakeTurns(); + } + + /** + * @dev ensures the upkeep is not cancelled and the caller is the upkeep admin + */ + function requireAdminAndNotCancelled(Upkeep memory upkeep) internal view { + if (msg.sender != upkeep.admin) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + } + + /** + * @dev generates a PerformParams struct for use in _performUpkeepWithParams() + */ + function _generatePerformParams( + address from, + uint256 id, + bytes memory performData, + bool isExecution + ) internal view returns (PerformParams memory) { + uint256 gasLimit = s_upkeep[id].executeGas; + (uint256 fastGasWei, uint256 linkEth) = _getFeedData(); + uint96 maxLinkPayment = _calculatePaymentAmount(gasLimit, fastGasWei, linkEth, isExecution); + + return + PerformParams({ + from: from, + id: id, + performData: performData, + maxLinkPayment: maxLinkPayment, + gasLimit: gasLimit, + fastGasWei: fastGasWei, + linkEth: linkEth + }); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistryLogic2_0.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistryLogic2_0.sol new file mode 100644 index 0000000..1c75aa5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/KeeperRegistryLogic2_0.sol @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "./KeeperRegistryBase2_0.sol"; +import "../../interfaces/MigratableKeeperRegistryInterface.sol"; +import "../../interfaces/UpkeepTranscoderInterface.sol"; + +/** + * @notice Logic contract, works in tandem with KeeperRegistry as a proxy + */ +contract KeeperRegistryLogic2_0 is KeeperRegistryBase2_0 { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + + /** + * @param paymentModel one of Default, Arbitrum, Optimism + * @param registryGasOverhead the gas overhead used by registry in performUpkeep + * @param link address of the LINK Token + * @param linkEthFeed address of the LINK/ETH price feed + * @param fastGasFeed address of the Fast Gas price feed + */ + constructor( + PaymentModel paymentModel, + uint256 registryGasOverhead, + address link, + address linkEthFeed, + address fastGasFeed + ) KeeperRegistryBase2_0(paymentModel, registryGasOverhead, link, linkEthFeed, fastGasFeed) {} + + function checkUpkeep(uint256 id, address from) + external + cannotExecute + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ) + { + Upkeep memory upkeep = s_upkeep[id]; + + bytes memory callData = abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[id]); + (bool success, bytes memory result) = upkeep.target.call{gas: s_storage.checkGasLimit}(callData); + + if (!success) revert TargetCheckReverted(result); + + (success, performData) = abi.decode(result, (bool, bytes)); + if (!success) revert UpkeepNotNeeded(); + + PerformParams memory params = _generatePerformParams(from, id, performData, false); + _prePerformUpkeep(upkeep, params.from, params.maxLinkPayment); + + return ( + performData, + params.maxLinkPayment, + params.gasLimit, + // adjustedGasWei equals fastGasWei multiplies gasCeilingMultiplier in non-execution cases + params.fastGasWei * s_storage.gasCeilingMultiplier, + params.linkEth + ); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function withdrawOwnerFunds() external onlyOwner { + uint96 amount = s_ownerLinkBalance; + + s_expectedLinkBalance = s_expectedLinkBalance - amount; + s_ownerLinkBalance = 0; + + emit OwnerFundsWithdrawn(amount); + LINK.transfer(msg.sender, amount); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function recoverFunds() external onlyOwner { + uint256 total = LINK.balanceOf(address(this)); + LINK.transfer(msg.sender, total - s_expectedLinkBalance); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function setKeepers(address[] calldata keepers, address[] calldata payees) external onlyOwner { + if (keepers.length != payees.length || keepers.length < 2) revert ParameterLengthError(); + for (uint256 i = 0; i < s_keeperList.length; i++) { + address keeper = s_keeperList[i]; + s_keeperInfo[keeper].active = false; + } + for (uint256 i = 0; i < keepers.length; i++) { + address keeper = keepers[i]; + KeeperInfo storage s_keeper = s_keeperInfo[keeper]; + address oldPayee = s_keeper.payee; + address newPayee = payees[i]; + if ( + (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) + ) revert InvalidPayee(); + if (s_keeper.active) revert DuplicateEntry(); + s_keeper.active = true; + if (newPayee != IGNORE_ADDRESS) { + s_keeper.payee = newPayee; + } + } + s_keeperList = keepers; + emit KeepersUpdated(keepers, payees); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function unpause() external onlyOwner { + _unpause(); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { + s_peerRegistryMigrationPermission[peer] = permission; + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id) { + if (msg.sender != owner() && msg.sender != s_registrar) revert OnlyCallableByOwnerOrRegistrar(); + + id = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), address(this), s_storage.nonce))); + _createUpkeep(id, target, gasLimit, admin, 0, checkData, false); + s_storage.nonce++; + emit UpkeepRegistered(id, gasLimit, admin); + return id; + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function cancelUpkeep(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + bool canceled = upkeep.maxValidBlocknumber != UINT32_MAX; + bool isOwner = msg.sender == owner(); + + if (canceled && !(isOwner && upkeep.maxValidBlocknumber > block.number)) revert CannotCancel(); + if (!isOwner && msg.sender != upkeep.admin) revert OnlyCallableByOwnerOrAdmin(); + + uint256 height = block.number; + if (!isOwner) { + height = height + CANCELLATION_DELAY; + } + s_upkeep[id].maxValidBlocknumber = uint32(height); + s_upkeepIDs.remove(id); + + // charge the cancellation fee if the minUpkeepSpend is not met + uint96 minUpkeepSpend = s_storage.minUpkeepSpend; + uint96 cancellationFee = 0; + // cancellationFee is supposed to be min(max(minUpkeepSpend - amountSpent,0), amountLeft) + if (upkeep.amountSpent < minUpkeepSpend) { + cancellationFee = minUpkeepSpend - upkeep.amountSpent; + if (cancellationFee > upkeep.balance) { + cancellationFee = upkeep.balance; + } + } + s_upkeep[id].balance = upkeep.balance - cancellationFee; + s_ownerLinkBalance = s_ownerLinkBalance + cancellationFee; + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function addFunds(uint256 id, uint96 amount) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + + s_upkeep[id].balance = upkeep.balance + amount; + s_expectedLinkBalance = s_expectedLinkBalance + amount; + LINK.transferFrom(msg.sender, address(this), amount); + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function withdrawFunds(uint256 id, address to) external { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber > block.number) revert UpkeepNotCanceled(); + + uint96 amountToWithdraw = s_upkeep[id].balance; + s_expectedLinkBalance = s_expectedLinkBalance - amountToWithdraw; + s_upkeep[id].balance = 0; + emit FundsWithdrawn(id, amountToWithdraw, to); + + LINK.transfer(to, amountToWithdraw); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external { + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (upkeep.admin != msg.sender) revert OnlyCallableByAdmin(); + + s_upkeep[id].executeGas = gasLimit; + + emit UpkeepGasLimitSet(id, gasLimit); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function withdrawPayment(address from, address to) external { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + KeeperInfo memory keeper = s_keeperInfo[from]; + if (keeper.payee != msg.sender) revert OnlyCallableByPayee(); + + s_keeperInfo[from].balance = 0; + s_expectedLinkBalance = s_expectedLinkBalance - keeper.balance; + emit PaymentWithdrawn(from, keeper.balance, to, msg.sender); + + LINK.transfer(to, keeper.balance); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function transferPayeeship(address keeper, address proposed) external { + if (s_keeperInfo[keeper].payee != msg.sender) revert OnlyCallableByPayee(); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedPayee[keeper] != proposed) { + s_proposedPayee[keeper] = proposed; + emit PayeeshipTransferRequested(keeper, msg.sender, proposed); + } + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function acceptPayeeship(address keeper) external { + if (s_proposedPayee[keeper] != msg.sender) revert OnlyCallableByProposedPayee(); + address past = s_keeperInfo[keeper].payee; + s_keeperInfo[keeper].payee = msg.sender; + s_proposedPayee[keeper] = ZERO_ADDRESS; + + emit PayeeshipTransferred(keeper, past, msg.sender); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function transferUpkeepAdmin(uint256 id, address proposed) external { + Upkeep memory upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + if (proposed == msg.sender) revert ValueNotChanged(); + if (proposed == ZERO_ADDRESS) revert InvalidRecipient(); + + if (s_proposedAdmin[id] != proposed) { + s_proposedAdmin[id] = proposed; + emit UpkeepAdminTransferRequested(id, msg.sender, proposed); + } + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function acceptUpkeepAdmin(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (s_proposedAdmin[id] != msg.sender) revert OnlyCallableByProposedAdmin(); + address past = upkeep.admin; + s_upkeep[id].admin = msg.sender; + s_proposedAdmin[id] = ZERO_ADDRESS; + + emit UpkeepAdminTransferred(id, past, msg.sender); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external { + if ( + s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && + s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + if (s_transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); + if (ids.length == 0) revert ArrayHasNoEntries(); + uint256 id; + Upkeep memory upkeep; + uint256 totalBalanceRemaining; + bytes[] memory checkDatas = new bytes[](ids.length); + Upkeep[] memory upkeeps = new Upkeep[](ids.length); + for (uint256 idx = 0; idx < ids.length; idx++) { + id = ids[idx]; + upkeep = s_upkeep[id]; + requireAdminAndNotCancelled(upkeep); + upkeeps[idx] = upkeep; + checkDatas[idx] = s_checkData[id]; + totalBalanceRemaining = totalBalanceRemaining + upkeep.balance; + delete s_upkeep[id]; + delete s_checkData[id]; + // nullify existing proposed admin change if an upkeep is being migrated + delete s_proposedAdmin[id]; + s_upkeepIDs.remove(id); + emit UpkeepMigrated(id, upkeep.balance, destination); + } + s_expectedLinkBalance = s_expectedLinkBalance - totalBalanceRemaining; + bytes memory encodedUpkeeps = abi.encode(ids, upkeeps, checkDatas); + MigratableKeeperRegistryInterface(destination).receiveUpkeeps( + UpkeepTranscoderInterface(s_transcoder).transcodeUpkeeps( + UPKEEP_TRANSCODER_VERSION_BASE, + MigratableKeeperRegistryInterface(destination).upkeepTranscoderVersion(), + encodedUpkeeps + ) + ); + LINK.transfer(destination, totalBalanceRemaining); + } + + /** + * @dev Called through KeeperRegistry main contract + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external { + if ( + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + (uint256[] memory ids, Upkeep[] memory upkeeps, bytes[] memory checkDatas) = abi.decode( + encodedUpkeeps, + (uint256[], Upkeep[], bytes[]) + ); + for (uint256 idx = 0; idx < ids.length; idx++) { + _createUpkeep( + ids[idx], + upkeeps[idx].target, + upkeeps[idx].executeGas, + upkeeps[idx].admin, + upkeeps[idx].balance, + checkDatas[idx], + upkeeps[idx].paused + ); + emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); + } + } + + /** + * @notice creates a new upkeep with the given fields + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data passed to the contract when checking for upkeep + * @param paused if this upkeep is paused + */ + function _createUpkeep( + uint256 id, + address target, + uint32 gasLimit, + address admin, + uint96 balance, + bytes memory checkData, + bool paused + ) internal whenNotPaused { + if (!target.isContract()) revert NotAContract(); + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + s_upkeep[id] = Upkeep({ + target: target, + executeGas: gasLimit, + balance: balance, + admin: admin, + maxValidBlocknumber: UINT32_MAX, + lastKeeper: ZERO_ADDRESS, + amountSpent: 0, + paused: paused + }); + s_expectedLinkBalance = s_expectedLinkBalance + balance; + s_checkData[id] = checkData; + s_upkeepIDs.add(id); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/interfaces/KeeperRegistryInterface2_0.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/interfaces/KeeperRegistryInterface2_0.sol new file mode 100644 index 0000000..773440f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/keeper2_0/interfaces/KeeperRegistryInterface2_0.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @notice config of the registry + * @dev only used in params and return values + * @member paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @member blockCountPerTurn number of blocks each oracle has during their turn to + * perform upkeep before it will be the next keeper's turn to submit + * @member checkGasLimit gas limit when checking for upkeep + * @member stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling + * @member maxPerformGas max executeGas allowed for an upkeep on this registry + * @member fallbackGasPrice gas price used if the gas price feed is stale + * @member fallbackLinkPrice LINK price used if the LINK price feed is stale + * @member transcoder address of the transcoder contract + * @member registrar address of the registrar contract + */ +struct Config { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address registrar; +} + +/** + * @notice state of the registry + * @dev only used in params and return values + * @member nonce used for ID generation + * @member ownerLinkBalance withdrawable balance of LINK by contract owner + * @member expectedLinkBalance the expected balance of LINK of the registry + * @member numUpkeeps total number of upkeeps on the registry + */ +struct State { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint256 numUpkeeps; +} + +/** + * @notice relevant state of an upkeep + * @member balance the balance of this upkeep + * @member lastKeeper the keeper which last performs the upkeep + * @member executeGas the gas limit of upkeep execution + * @member maxValidBlocknumber until which block this upkeep is valid + * @member target the contract which needs to be serviced + * @member amountSpent the amount this upkeep has spent + * @member admin the upkeep admin + * @member paused if this upkeep has been paused + */ +struct Upkeep { + uint96 balance; + address lastKeeper; // 1 full evm word + uint96 amountSpent; + address admin; // 2 full evm words + uint32 executeGas; + uint32 maxValidBlocknumber; + address target; + bool paused; // 24 bits to 3 full evm words +} + +interface KeeperRegistryBaseInterface { + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id); + + function performUpkeep(uint256 id, bytes calldata performData) external returns (bool success); + + function cancelUpkeep(uint256 id) external; + + function pauseUpkeep(uint256 id) external; + + function unpauseUpkeep(uint256 id) external; + + function transferUpkeepAdmin(uint256 id, address proposed) external; + + function acceptUpkeepAdmin(uint256 id) external; + + function updateCheckData(uint256 id, bytes calldata newCheckData) external; + + function addFunds(uint256 id, uint96 amount) external; + + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; + + function getUpkeep(uint256 id) + external + view + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent, + bool paused + ); + + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); + + function getKeeperInfo(address query) + external + view + returns ( + address payee, + bool active, + uint96 balance + ); + + function getState() + external + view + returns ( + State memory, + Config memory, + address[] memory + ); +} + +/** + * @dev The view methods are not actually marked as view in the implementation + * but we want them to be easily queried off-chain. Solidity will not compile + * if we actually inherit from this interface, so we document it here. + */ +interface KeeperRegistryInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + view + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + int256 gasWei, + int256 linkEth + ); +} + +interface KeeperRegistryExecutableInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/OCR2Abstract.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/OCR2Abstract.sol new file mode 100644 index 0000000..f07ee06 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/OCR2Abstract.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../../interfaces/TypeAndVersionInterface.sol"; + +abstract contract OCR2Abstract is TypeAndVersionInterface { + // Maximum number of oracles the offchain reporting protocol is designed for + uint256 internal constant maxNumOracles = 31; + + /** + * @notice triggers a new run of the offchain reporting protocol + * @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis + * @param configDigest configDigest of this configuration + * @param configCount ordinal number of this config setting among all config settings over the life of this contract + * @param signers ith element is address ith oracle uses to sign a report + * @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method + * @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly + * @param onchainConfig serialized configuration used by the contract (and possibly oracles) + * @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter + * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract + */ + event ConfigSet( + uint32 previousConfigBlockNumber, + bytes32 configDigest, + uint64 configCount, + address[] signers, + address[] transmitters, + uint8 f, + bytes onchainConfig, + uint64 offchainConfigVersion, + bytes offchainConfig + ); + + /** + * @notice sets offchain reporting protocol configuration incl. participating oracles + * @param signers addresses with which oracles sign the reports + * @param transmitters addresses oracles use to transmit the reports + * @param f number of faulty oracles the system can tolerate + * @param onchainConfig serialized configuration used by the contract (and possibly oracles) + * @param offchainConfigVersion version number for offchainEncoding schema + * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract + */ + function setConfig( + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external virtual; + + /** + * @notice information about current offchain reporting protocol configuration + * @return configCount ordinal number of current config, out of all configs applied to this contract so far + * @return blockNumber block at which this config was set + * @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) + */ + function latestConfigDetails() + external + view + virtual + returns ( + uint32 configCount, + uint32 blockNumber, + bytes32 configDigest + ); + + function _configDigestFromConfigData( + uint256 chainId, + address contractAddress, + uint64 configCount, + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) internal pure returns (bytes32) { + uint256 h = uint256( + keccak256( + abi.encode( + chainId, + contractAddress, + configCount, + signers, + transmitters, + f, + onchainConfig, + offchainConfigVersion, + offchainConfig + ) + ) + ); + uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 + return bytes32((prefix & prefixMask) | (h & ~prefixMask)); + } + + /** + * @notice optionally emited to indicate the latest configDigest and epoch for + which a report was successfully transmited. Alternatively, the contract may + use latestConfigDigestAndEpoch with scanLogs set to false. + */ + event Transmitted(bytes32 configDigest, uint32 epoch); + + /** + * @notice optionally returns the latest configDigest and epoch for which a + report was successfully transmitted. Alternatively, the contract may return + scanLogs set to true and use Transmitted events to provide this information + to offchain watchers. + * @return scanLogs indicates whether to rely on the configDigest and epoch + returned or whether to scan logs for the Transmitted event instead. + * @return configDigest + * @return epoch + */ + function latestConfigDigestAndEpoch() + external + view + virtual + returns ( + bool scanLogs, + bytes32 configDigest, + uint32 epoch + ); + + /** + * @notice transmit is called to post a new report to the contract + * @param report serialized report, which the signatures are signing. + * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries + * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries + * @param rawVs ith element is the the V component of the ith signature + */ + function transmit( + // NOTE: If these parameters are changed, expectedMsgDataLength and/or + // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly + bytes32[3] calldata reportContext, + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs // signatures + ) external virtual; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/OCR2Base.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/OCR2Base.sol new file mode 100644 index 0000000..6b02946 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/OCR2Base.sol @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../../ConfirmedOwner.sol"; +import "./OCR2Abstract.sol"; + +/** + * @notice Onchain verification of reports from the offchain reporting protocol + * @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. + * @dev For details on its operation, see the offchain reporting protocol design + * doc, which refers to this contract as simply the "contract". + * @dev This contract is meant to aid rapid development of new applications based on OCR2. + * However, for actual production contracts, it is expected that most of the logic of this contract + * will be folded directly into the application contract. Inheritance prevents us from doing lots + * of juicy storage layout optimizations, leading to a substantial increase in gas cost. + */ +abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { + bool internal immutable i_uniqueReports; + + constructor(bool uniqueReports) ConfirmedOwner(msg.sender) { + i_uniqueReports = uniqueReports; + } + + uint256 private constant maxUint32 = (1 << 32) - 1; + + // Storing these fields used on the hot path in a ConfigInfo variable reduces the + // retrieval of all of them to a single SLOAD. If any further fields are + // added, make sure that storage of the struct still takes at most 32 bytes. + struct ConfigInfo { + bytes32 latestConfigDigest; + uint8 f; // TODO: could be optimized by squeezing into one slot + uint8 n; + } + ConfigInfo internal s_configInfo; + + // incremented each time a new config is posted. This count is incorporated + // into the config digest, to prevent replay attacks. + uint32 internal s_configCount; + uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems + // to extract config from logs. + + // Used for s_oracles[a].role, where a is an address, to track the purpose + // of the address, or to indicate that the address is unset. + enum Role { + // No oracle role has been set for address a + Unset, + // Signing address for the s_oracles[a].index'th oracle. I.e., report + // signatures from this oracle should ecrecover back to address a. + Signer, + // Transmission address for the s_oracles[a].index'th oracle. I.e., if a + // report is received by OCR2Aggregator.transmit in which msg.sender is + // a, it is attributed to the s_oracles[a].index'th oracle. + Transmitter + } + + struct Oracle { + uint8 index; // Index of oracle in s_signers/s_transmitters + Role role; // Role of the address which mapped to this struct + } + + mapping(address => Oracle) /* signer OR transmitter address */ + internal s_oracles; + + // s_signers contains the signing address of each oracle + address[] internal s_signers; + + // s_transmitters contains the transmission address of each oracle, + // i.e. the address the oracle actually sends transactions to the contract from + address[] internal s_transmitters; + + /* + * Config logic + */ + + // Reverts transaction if config args are invalid + modifier checkConfigValid( + uint256 _numSigners, + uint256 _numTransmitters, + uint256 _f + ) { + require(_numSigners <= maxNumOracles, "too many signers"); + require(_f > 0, "f must be positive"); + require(_numSigners == _numTransmitters, "oracle addresses out of registration"); + require(_numSigners > 3 * _f, "faulty-oracle f too high"); + _; + } + + struct SetConfigArgs { + address[] signers; + address[] transmitters; + uint8 f; + bytes onchainConfig; + uint64 offchainConfigVersion; + bytes offchainConfig; + } + + /// @inheritdoc OCR2Abstract + function latestConfigDigestAndEpoch() + external + view + virtual + override + returns ( + bool scanLogs, + bytes32 configDigest, + uint32 epoch + ) + { + return (true, bytes32(0), uint32(0)); + } + + /** + * @notice sets offchain reporting protocol configuration incl. participating oracles + * @param _signers addresses with which oracles sign the reports + * @param _transmitters addresses oracles use to transmit the reports + * @param _f number of faulty oracles the system can tolerate + * @param _onchainConfig encoded on-chain contract configuration + * @param _offchainConfigVersion version number for offchainEncoding schema + * @param _offchainConfig encoded off-chain oracle configuration + */ + function setConfig( + address[] memory _signers, + address[] memory _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _offchainConfigVersion, + bytes memory _offchainConfig + ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { + SetConfigArgs memory args = SetConfigArgs({ + signers: _signers, + transmitters: _transmitters, + f: _f, + onchainConfig: _onchainConfig, + offchainConfigVersion: _offchainConfigVersion, + offchainConfig: _offchainConfig + }); + + _beforeSetConfig(args.f, args.onchainConfig); + + while (s_signers.length != 0) { + // remove any old signer/transmitter addresses + uint256 lastIdx = s_signers.length - 1; + address signer = s_signers[lastIdx]; + address transmitter = s_transmitters[lastIdx]; + delete s_oracles[signer]; + delete s_oracles[transmitter]; + s_signers.pop(); + s_transmitters.pop(); + } + + for (uint256 i = 0; i < args.signers.length; ++i) { + // add new signer/transmitter addresses + require(s_oracles[args.signers[i]].role == Role.Unset, "repeated signer address"); + s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); + require(s_oracles[args.transmitters[i]].role == Role.Unset, "repeated transmitter address"); + s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); + s_signers.push(args.signers[i]); + s_transmitters.push(args.transmitters[i]); + } + s_configInfo.f = args.f; + uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; + s_latestConfigBlockNumber = uint32(block.number); + s_configCount += 1; + { + s_configInfo.latestConfigDigest = configDigestFromConfigData( + block.chainid, + address(this), + s_configCount, + args.signers, + args.transmitters, + args.f, + args.onchainConfig, + args.offchainConfigVersion, + args.offchainConfig + ); + } + s_configInfo.n = uint8(args.signers.length); + + emit ConfigSet( + previousConfigBlockNumber, + s_configInfo.latestConfigDigest, + s_configCount, + args.signers, + args.transmitters, + args.f, + args.onchainConfig, + args.offchainConfigVersion, + args.offchainConfig + ); + + _afterSetConfig(args.f, args.onchainConfig); + } + + function configDigestFromConfigData( + uint256 _chainId, + address _contractAddress, + uint64 _configCount, + address[] memory _signers, + address[] memory _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _encodedConfigVersion, + bytes memory _encodedConfig + ) internal pure returns (bytes32) { + uint256 h = uint256( + keccak256( + abi.encode( + _chainId, + _contractAddress, + _configCount, + _signers, + _transmitters, + _f, + _onchainConfig, + _encodedConfigVersion, + _encodedConfig + ) + ) + ); + uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 + return bytes32((prefix & prefixMask) | (h & ~prefixMask)); + } + + /** + * @notice information about current offchain reporting protocol configuration + * @return configCount ordinal number of current config, out of all configs applied to this contract so far + * @return blockNumber block at which this config was set + * @return configDigest domain-separation tag for current config (see configDigestFromConfigData) + */ + function latestConfigDetails() + external + view + override + returns ( + uint32 configCount, + uint32 blockNumber, + bytes32 configDigest + ) + { + return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); + } + + /** + * @return list of addresses permitted to transmit reports to this contract + * @dev The list will match the order used to specify the transmitter during setConfig + */ + function transmitters() external view returns (address[] memory) { + return s_transmitters; + } + + function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; + + function _afterSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; + + function _report( + bytes32 configDigest, + uint40 epochAndRound, + bytes memory report + ) internal virtual; + + function _payTransmitter(uint32 initialGas, address transmitter) internal virtual; + + // The constant-length components of the msg.data sent to transmit. + // See the "If we wanted to call sam" example on for example reasoning + // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html + uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = + 4 + // function selector + 32 * + 3 + // 3 words containing reportContext + 32 + // word containing start location of abiencoded report value + 32 + // word containing location start of abiencoded rs value + 32 + // word containing start location of abiencoded ss value + 32 + // rawVs value + 32 + // word containing length of report + 32 + // word containing length rs + 32 + // word containing length of ss + 0; // placeholder + + function requireExpectedMsgDataLength( + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss + ) private pure { + // calldata will never be big enough to make this overflow + uint256 expected = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + + report.length + // one byte pure entry in _report + rs.length * + 32 + // 32 bytes per entry in _rs + ss.length * + 32 + // 32 bytes per entry in _ss + 0; // placeholder + require(msg.data.length == expected, "calldata length mismatch"); + } + + /** + * @notice transmit is called to post a new report to the contract + * @param report serialized report, which the signatures are signing. + * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries + * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries + * @param rawVs ith element is the the V component of the ith signature + */ + function transmit( + // NOTE: If these parameters are changed, expectedMsgDataLength and/or + // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly + bytes32[3] calldata reportContext, + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs // signatures + ) external override { + uint256 initialGas = gasleft(); // This line must come first + + { + // reportContext consists of: + // reportContext[0]: ConfigDigest + // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round + // reportContext[2]: ExtraHash + bytes32 configDigest = reportContext[0]; + uint32 epochAndRound = uint32(uint256(reportContext[1])); + + _report(configDigest, epochAndRound, report); + + emit Transmitted(configDigest, uint32(epochAndRound >> 8)); + + ConfigInfo memory configInfo = s_configInfo; + require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); + + requireExpectedMsgDataLength(report, rs, ss); + + uint256 expectedNumSignatures; + if (i_uniqueReports) { + expectedNumSignatures = (configInfo.n + configInfo.f) / 2 + 1; + } else { + expectedNumSignatures = configInfo.f + 1; + } + + require(rs.length == expectedNumSignatures, "wrong number of signatures"); + require(rs.length == ss.length, "signatures out of registration"); + + Oracle memory transmitter = s_oracles[msg.sender]; + require( // Check that sender is authorized to report + transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index], + "unauthorized transmitter" + ); + } + + { + // Verify signatures attached to report + bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); + bool[maxNumOracles] memory signed; + + Oracle memory o; + for (uint256 i = 0; i < rs.length; ++i) { + address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); + o = s_oracles[signer]; + require(o.role == Role.Signer, "address not authorized to sign"); + require(!signed[o.index], "non-unique signature"); + signed[o.index] = true; + } + } + + assert(initialGas < maxUint32); + _payTransmitter(uint32(initialGas), msg.sender); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/README.md b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/README.md new file mode 100644 index 0000000..e7ccc51 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/ocr2/README.md @@ -0,0 +1,5 @@ +# OCR2 Rapid Prototype Contracts + +The contracts in this directory are to aid rapid prototyping of OCR2 based products. They abstract OCR2 config and boilerplate code so that specific logic can be implemented and tested quickly. They are not optimized or audited. + +Do not use these contracts in production. For actual production contracts, it is expected that most of the logic of these contracts will be folded directly into the application contract. \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol new file mode 100644 index 0000000..e274170 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol @@ -0,0 +1,39 @@ +pragma solidity >=0.4.21 <0.9.0; + +interface ArbGasInfo { + // return gas prices in wei, assuming the specified aggregator is used + // ( + // per L2 tx, + // per L1 calldata unit, (zero byte = 4 units, nonzero byte = 16 units) + // per storage allocation, + // per ArbGas base, + // per ArbGas congestion, + // per ArbGas total + // ) + function getPricesInWeiWithAggregator(address aggregator) external view returns (uint, uint, uint, uint, uint, uint); + + // return gas prices in wei, as described above, assuming the caller's preferred aggregator is used + // if the caller hasn't specified a preferred aggregator, the default aggregator is assumed + function getPricesInWei() external view returns (uint, uint, uint, uint, uint, uint); + + // return prices in ArbGas (per L2 tx, per L1 calldata unit, per storage allocation), + // assuming the specified aggregator is used + function getPricesInArbGasWithAggregator(address aggregator) external view returns (uint, uint, uint); + + // return gas prices in ArbGas, as described above, assuming the caller's preferred aggregator is used + // if the caller hasn't specified a preferred aggregator, the default aggregator is assumed + function getPricesInArbGas() external view returns (uint, uint, uint); + + // return gas accounting parameters (speedLimitPerSecond, gasPoolMax, maxTxGasLimit) + function getGasAccountingParams() external view returns (uint, uint, uint); + + // get ArbOS's estimate of the L1 gas price in wei + function getL1GasPriceEstimate() external view returns(uint); + + // set ArbOS's estimate of the L1 gas price in wei + // reverts unless called by chain owner or designated gas oracle (if any) + function setL1GasPriceEstimate(uint priceInWei) external; + + // get L1 gas fees paid by the current transaction (txBaseFeeWei, calldataFeeWei) + function getCurrentTxL1GasFees() external view returns(uint); +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol new file mode 100644 index 0000000..6d74788 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@eth-optimism/contracts/0.4.7/contracts/optimistic-ethereum/iOVM/bridge/messaging/iOVM_CrossDomainMessenger.sol @@ -0,0 +1,36 @@ +pragma solidity >=0.7.6 <0.9.0; + +/** + * @title iOVM_CrossDomainMessenger + */ +interface iOVM_CrossDomainMessenger { + /********** + * Events * + **********/ + + event SentMessage(bytes message); + event RelayedMessage(bytes32 msgHash); + event FailedRelayedMessage(bytes32 msgHash); + + /************* + * Variables * + *************/ + + function xDomainMessageSender() external view returns (address); + + /******************** + * Public Functions * + ********************/ + + /** + * Sends a cross domain message to the target messenger. + * @param _target Target contract address. + * @param _message Message to send to the target. + * @param _gasLimit Gas limit for the provided message. + */ + function sendMessage( + address _target, + bytes calldata _message, + uint32 _gasLimit + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@eth-optimism/contracts/0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@eth-optimism/contracts/0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol new file mode 100644 index 0000000..2ab825f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/@eth-optimism/contracts/0.8.6/contracts/L2/predeploys/OVM_GasPriceOracle.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; + +/* External Imports */ +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title OVM_GasPriceOracle + * @dev This contract exposes the current l2 gas price, a measure of how congested the network + * currently is. This measure is used by the Sequencer to determine what fee to charge for + * transactions. When the system is more congested, the l2 gas price will increase and fees + * will also increase as a result. + * + * All public variables are set while generating the initial L2 state. The + * constructor doesn't run in practice as the L2 state generation script uses + * the deployed bytecode instead of running the initcode. + */ +contract OVM_GasPriceOracle is Ownable { + /************* + * Variables * + *************/ + + // Current L2 gas price + uint256 public gasPrice; + // Current L1 base fee + uint256 public l1BaseFee; + // Amortized cost of batch submission per transaction + uint256 public overhead; + // Value to scale the fee up by + uint256 public scalar; + // Number of decimals of the scalar + uint256 public decimals; + + /*************** + * Constructor * + ***************/ + + /** + * @param _owner Address that will initially own this contract. + */ + constructor(address _owner) Ownable() { + transferOwnership(_owner); + } + + /********** + * Events * + **********/ + + event GasPriceUpdated(uint256); + event L1BaseFeeUpdated(uint256); + event OverheadUpdated(uint256); + event ScalarUpdated(uint256); + event DecimalsUpdated(uint256); + + /******************** + * Public Functions * + ********************/ + + /** + * Allows the owner to modify the l2 gas price. + * @param _gasPrice New l2 gas price. + */ + // slither-disable-next-line external-function + function setGasPrice(uint256 _gasPrice) public onlyOwner { + gasPrice = _gasPrice; + emit GasPriceUpdated(_gasPrice); + } + + /** + * Allows the owner to modify the l1 base fee. + * @param _baseFee New l1 base fee + */ + // slither-disable-next-line external-function + function setL1BaseFee(uint256 _baseFee) public onlyOwner { + l1BaseFee = _baseFee; + emit L1BaseFeeUpdated(_baseFee); + } + + /** + * Allows the owner to modify the overhead. + * @param _overhead New overhead + */ + // slither-disable-next-line external-function + function setOverhead(uint256 _overhead) public onlyOwner { + overhead = _overhead; + emit OverheadUpdated(_overhead); + } + + /** + * Allows the owner to modify the scalar. + * @param _scalar New scalar + */ + // slither-disable-next-line external-function + function setScalar(uint256 _scalar) public onlyOwner { + scalar = _scalar; + emit ScalarUpdated(_scalar); + } + + /** + * Allows the owner to modify the decimals. + * @param _decimals New decimals + */ + // slither-disable-next-line external-function + function setDecimals(uint256 _decimals) public onlyOwner { + decimals = _decimals; + emit DecimalsUpdated(_decimals); + } + + /** + * Computes the L1 portion of the fee + * based on the size of the RLP encoded tx + * and the current l1BaseFee + * @param _data Unsigned RLP encoded tx, 6 elements + * @return L1 fee that should be paid for the tx + */ + // slither-disable-next-line external-function + function getL1Fee(bytes memory _data) public view returns (uint256) { + uint256 l1GasUsed = getL1GasUsed(_data); + uint256 l1Fee = l1GasUsed * l1BaseFee; + uint256 divisor = 10**decimals; + uint256 unscaled = l1Fee * scalar; + uint256 scaled = unscaled / divisor; + return scaled; + } + + // solhint-disable max-line-length + /** + * Computes the amount of L1 gas used for a transaction + * The overhead represents the per batch gas overhead of + * posting both transaction and state roots to L1 given larger + * batch sizes. + * 4 gas for 0 byte + * https://github.com/ethereum/go-ethereum/blob/9ada4a2e2c415e6b0b51c50e901336872e028872/params/protocol_params.go#L33 + * 16 gas for non zero byte + * https://github.com/ethereum/go-ethereum/blob/9ada4a2e2c415e6b0b51c50e901336872e028872/params/protocol_params.go#L87 + * This will need to be updated if calldata gas prices change + * Account for the transaction being unsigned + * Padding is added to account for lack of signature on transaction + * 1 byte for RLP V prefix + * 1 byte for V + * 1 byte for RLP R prefix + * 32 bytes for R + * 1 byte for RLP S prefix + * 32 bytes for S + * Total: 68 bytes of padding + * @param _data Unsigned RLP encoded tx, 6 elements + * @return Amount of L1 gas used for a transaction + */ + // solhint-enable max-line-length + function getL1GasUsed(bytes memory _data) public view returns (uint256) { + uint256 total = 0; + for (uint256 i = 0; i < _data.length; i++) { + if (_data[i] == 0) { + total += 4; + } else { + total += 16; + } + } + uint256 unsigned = total + overhead; + return unsigned + (68 * 16); + } +} \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol new file mode 100644 index 0000000..3d9e757 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTICE: pragma change from original (^0.6.11) +pragma solidity ^0.8.0; + +interface IBridge { + event MessageDelivered( + uint256 indexed messageIndex, + bytes32 indexed beforeInboxAcc, + address inbox, + uint8 kind, + address sender, + bytes32 messageDataHash + ); + + function deliverMessageToInbox( + uint8 kind, + address sender, + bytes32 messageDataHash + ) external payable returns (uint256); + + function executeCall( + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (bool success, bytes memory returnData); + + // These are only callable by the admin + function setInbox(address inbox, bool enabled) external; + + function setOutbox(address inbox, bool enabled) external; + + // View functions + + function activeOutbox() external view returns (address); + + function allowedInboxes(address inbox) external view returns (bool); + + function allowedOutboxes(address outbox) external view returns (bool); + + function inboxAccs(uint256 index) external view returns (bytes32); + + function messageCount() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol new file mode 100644 index 0000000..b83fcec --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTICE: pragma change from original (^0.6.11) +pragma solidity ^0.8.0; + +import "./IBridge.sol"; +import "./IMessageProvider.sol"; + +interface IInbox is IMessageProvider { + function sendL2Message(bytes calldata messageData) external returns (uint256); + + function sendUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function sendContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function sendL1FundedUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + bytes calldata data + ) external payable returns (uint256); + + function sendL1FundedContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + bytes calldata data + ) external payable returns (uint256); + + function createRetryableTicketNoRefundAliasRewrite( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable returns (uint256); + + function createRetryableTicket( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable returns (uint256); + + function depositEth(address destAddr) external payable returns (uint256); + + function depositEthRetryable( + address destAddr, + uint256 maxSubmissionCost, + uint256 maxGas, + uint256 maxGasPrice + ) external payable returns (uint256); + + function bridge() external view returns (IBridge); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IMessageProvider.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IMessageProvider.sol new file mode 100644 index 0000000..a29dc65 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IMessageProvider.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTICE: pragma change from original (^0.6.11) +pragma solidity ^0.8.0; + +interface IMessageProvider { + event InboxMessageDelivered(uint256 indexed messageNum, bytes data); + + event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol new file mode 100644 index 0000000..dd3d544 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2019-2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTICE: pragma change from original (^0.6.11) +pragma solidity ^0.8.0; + +library AddressAliasHelper { + uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l1Address L2 address as viewed in msg.sender + /// @return l2Address the address in the L1 that triggered the tx to L2 + function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + unchecked { + l2Address = address(uint160(l1Address) + offset); + } + } + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { + unchecked { + l1Address = address(uint160(l2Address) - offset); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-os/e8d9696f21/contracts/arbos/builtin/ArbSys.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-os/e8d9696f21/contracts/arbos/builtin/ArbSys.sol new file mode 100644 index 0000000..7c89f0a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/arb-os/e8d9696f21/contracts/arbos/builtin/ArbSys.sol @@ -0,0 +1,72 @@ +// NOTICE: pragma change from original (>=0.4.21 <0.7.0) +pragma solidity >=0.4.21 <0.9.0; + +/** + * @title Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. Exposes a variety of system-level functionality. + */ +interface ArbSys { + /** + * @notice Get internal version number identifying an ArbOS build + * @return version number as int + */ + function arbOSVersion() external pure returns (uint256); + + function arbChainID() external view returns (uint256); + + /** + * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) + * @return block number as int + */ + function arbBlockNumber() external view returns (uint256); + + /** + * @notice Send given amount of Eth to dest from sender. + * This is a convenience function, which is equivalent to calling sendTxToL1 with empty calldataForL1. + * @param destination recipient address on L1 + * @return unique identifier for this L2-to-L1 transaction. + */ + function withdrawEth(address destination) external payable returns (uint256); + + /** + * @notice Send a transaction to L1 + * @param destination recipient address on L1 + * @param calldataForL1 (optional) calldata for L1 contract call + * @return a unique identifier for this L2-to-L1 transaction. + */ + function sendTxToL1(address destination, bytes calldata calldataForL1) external payable returns (uint256); + + /** + * @notice get the number of transactions issued by the given external account or the account sequence number of the given contract + * @param account target account + * @return the number of transactions issued by the given external account or the account sequence number of the given contract + */ + function getTransactionCount(address account) external view returns (uint256); + + /** + * @notice get the value of target L2 storage slot + * This function is only callable from address 0 to prevent contracts from being able to call it + * @param account target account + * @param index target index of storage slot + * @return stotage value for the given account at the given index + */ + function getStorageAt(address account, uint256 index) external view returns (uint256); + + /** + * @notice check if current call is coming from l1 + * @return true if the caller of this was called directly from L1 + */ + function isTopLevelCall() external view returns (bool); + + event L2ToL1Transaction( + address caller, + address indexed destination, + uint256 indexed uniqueId, + uint256 indexed batchNumber, + uint256 indexInBatch, + uint256 arbBlockNum, + uint256 ethBlockNum, + uint256 timestamp, + uint256 callvalue, + bytes data + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol new file mode 100644 index 0000000..5b9c5b3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + assembly { + size := extcodesize(account) + } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/factories/CronUpkeepFactory.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/factories/CronUpkeepFactory.sol new file mode 100644 index 0000000..5a2c8ff --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/factories/CronUpkeepFactory.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import "../upkeeps/CronUpkeep.sol"; +import "../upkeeps/CronUpkeepDelegate.sol"; +import "../ConfirmedOwner.sol"; +import {Spec, Cron as CronExternal} from "../libraries/external/Cron.sol"; + +/** + * @title The CronUpkeepFactory contract + * @notice This contract serves as a delegate for all instances of CronUpkeep. Those contracts + * delegate their checkUpkeep calls onto this contract. Utilizing this pattern reduces the size + * of the CronUpkeep contracts. + */ +contract CronUpkeepFactory is ConfirmedOwner { + event NewCronUpkeepCreated(address upkeep, address owner); + + address private immutable s_cronDelegate; + uint256 public s_maxJobs = 5; + + constructor() ConfirmedOwner(msg.sender) { + s_cronDelegate = address(new CronUpkeepDelegate()); + } + + /** + * @notice Creates a new CronUpkeep contract, with msg.sender as the owner + */ + function newCronUpkeep() external { + newCronUpkeepWithJob(bytes("")); + } + + /** + * @notice Creates a new CronUpkeep contract, with msg.sender as the owner, and registers a cron job + */ + function newCronUpkeepWithJob(bytes memory encodedJob) public { + emit NewCronUpkeepCreated(address(new CronUpkeep(msg.sender, s_cronDelegate, s_maxJobs, encodedJob)), msg.sender); + } + + /** + * @notice Sets the max job limit on new cron upkeeps + */ + function setMaxJobs(uint256 maxJobs) external onlyOwner { + s_maxJobs = maxJobs; + } + + /** + * @notice Gets the address of the delegate contract + * @return the address of the delegate contract + */ + function cronDelegateAddress() external view returns (address) { + return s_cronDelegate; + } + + /** + * @notice Converts a cron string to a Spec, validates the spec, and encodes the spec. + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to convert and encode + * @return the abi encoding of the Spec struct representing the cron string + */ + function encodeCronString(string memory cronString) external pure returns (bytes memory) { + return CronExternal.toEncodedSpec(cronString); + } + + /** + * @notice Converts, validates, and encodes a full cron spec. This payload is then passed to newCronUpkeepWithJob. + * @param target the destination contract of a cron job + * @param handler the function signature on the target contract to call + * @param cronString the cron string to convert and encode + * @return the abi encoding of the entire cron job + */ + function encodeCronJob( + address target, + bytes memory handler, + string memory cronString + ) external pure returns (bytes memory) { + Spec memory spec = CronExternal.toSpec(cronString); + return abi.encode(target, handler, spec); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AccessControllerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AccessControllerInterface.sol new file mode 100644 index 0000000..fa3e2a5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AccessControllerInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface AccessControllerInterface { + function hasAccess(address user, bytes calldata data) external view returns (bool); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorInterface.sol new file mode 100644 index 0000000..fe0cef0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorInterface.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface AggregatorInterface { + function latestAnswer() external view returns (int256); + + function latestTimestamp() external view returns (uint256); + + function latestRound() external view returns (uint256); + + function getAnswer(uint256 roundId) external view returns (int256); + + function getTimestamp(uint256 roundId) external view returns (uint256); + + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); + + event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol new file mode 100644 index 0000000..4cb40c1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./AggregatorInterface.sol"; +import "./AggregatorV3Interface.sol"; + +interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol new file mode 100644 index 0000000..cd72a0c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface AggregatorV3Interface { + function decimals() external view returns (uint8); + + function description() external view returns (string memory); + + function version() external view returns (uint256); + + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorValidatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorValidatorInterface.sol new file mode 100644 index 0000000..7e3b7a3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorValidatorInterface.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface AggregatorValidatorInterface { + function validate( + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ) external returns (bool); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol new file mode 100644 index 0000000..a60e3f9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface AutomationCompatibleInterface { + /** + * @notice method that is simulated by the keepers to see if any work actually + * needs to be performed. This method does does not actually need to be + * executable, and since it is only ever simulated it can consume lots of gas. + * @dev To ensure that it is never called, you may want to add the + * cannotExecute modifier from KeeperBase to your implementation of this + * method. + * @param checkData specified in the upkeep registration so it is always the + * same for a registered upkeep. This can easily be broken down into specific + * arguments using `abi.decode`, so multiple upkeeps can be registered on the + * same contract and easily differentiated by the contract. + * @return upkeepNeeded boolean to indicate whether the keeper should call + * performUpkeep or not. + * @return performData bytes that the keeper should call performUpkeep with, if + * upkeep is needed. If you would like to encode data to decode later, try + * `abi.encode`. + */ + function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData); + + /** + * @notice method that is actually executed by the keepers, via the registry. + * The data returned by the checkUpkeep simulation will be passed into + * this method to actually be executed. + * @dev The input to this method should not be trusted, and the caller of the + * method should not even be restricted to any single registry. Anyone should + * be able call it, and the input should be validated, there is no guarantee + * that the data passed in is the performData returned from checkUpkeep. This + * could happen due to malicious keepers, racing keepers, or simply a state + * change while the performUpkeep transaction is waiting for confirmation. + * Always validate the data passed in. + * @param performData is the data which was passed back from the checkData + * simulation. If it is encoded, it can easily be decoded into other types by + * calling `abi.decode`. This data should not be trusted, and should be + * validated against the contract's current state. + */ + function performUpkeep(bytes calldata performData) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AutomationRegistryInterface1_2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AutomationRegistryInterface1_2.sol new file mode 100644 index 0000000..d72d527 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AutomationRegistryInterface1_2.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @notice config of the registry + * @dev only used in params and return values + * @member paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @member blockCountPerTurn number of blocks each oracle has during their turn to + * perform upkeep before it will be the next keeper's turn to submit + * @member checkGasLimit gas limit when checking for upkeep + * @member stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling + * @member maxPerformGas max executeGas allowed for an upkeep on this registry + * @member fallbackGasPrice gas price used if the gas price feed is stale + * @member fallbackLinkPrice LINK price used if the LINK price feed is stale + * @member transcoder address of the transcoder contract + * @member registrar address of the registrar contract + */ +struct Config { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address registrar; +} + +/** + * @notice state of the registry + * @dev only used in params and return values + * @member nonce used for ID generation + * @member ownerLinkBalance withdrawable balance of LINK by contract owner + * @member expectedLinkBalance the expected balance of LINK of the registry + * @member numUpkeeps total number of upkeeps on the registry + */ +struct State { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint256 numUpkeeps; +} + +interface AutomationRegistryBaseInterface { + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id); + + function performUpkeep(uint256 id, bytes calldata performData) external returns (bool success); + + function cancelUpkeep(uint256 id) external; + + function addFunds(uint256 id, uint96 amount) external; + + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; + + function getUpkeep(uint256 id) + external + view + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent + ); + + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); + + function getKeeperInfo(address query) + external + view + returns ( + address payee, + bool active, + uint96 balance + ); + + function getState() + external + view + returns ( + State memory, + Config memory, + address[] memory + ); +} + +/** + * @dev The view methods are not actually marked as view in the implementation + * but we want them to be easily queried off-chain. Solidity will not compile + * if we actually inherit from this interface, so we document it here. + */ +interface AutomationRegistryInterface is AutomationRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + view + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + int256 gasWei, + int256 linkEth + ); +} + +interface AutomationRegistryExecutableInterface is AutomationRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/BlockhashStoreInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/BlockhashStoreInterface.sol new file mode 100644 index 0000000..8177557 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/BlockhashStoreInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface BlockhashStoreInterface { + function getBlockhash(uint256 number) external view returns (bytes32); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ChainlinkRequestInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ChainlinkRequestInterface.sol new file mode 100644 index 0000000..62e443f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ChainlinkRequestInterface.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ChainlinkRequestInterface { + function oracleRequest( + address sender, + uint256 requestPrice, + bytes32 serviceAgreementID, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external; + + function cancelOracleRequest( + bytes32 requestId, + uint256 payment, + bytes4 callbackFunctionId, + uint256 expiration + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ENSInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ENSInterface.sol new file mode 100644 index 0000000..8695ec9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ENSInterface.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ENSInterface { + // Logged when the owner of a node assigns a new owner to a subnode. + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + + // Logged when the owner of a node transfers ownership to a new account. + event Transfer(bytes32 indexed node, address owner); + + // Logged when the resolver for a node changes. + event NewResolver(bytes32 indexed node, address resolver); + + // Logged when the TTL of a node changes + event NewTTL(bytes32 indexed node, uint64 ttl); + + function setSubnodeOwner( + bytes32 node, + bytes32 label, + address owner + ) external; + + function setResolver(bytes32 node, address resolver) external; + + function setOwner(bytes32 node, address owner) external; + + function setTTL(bytes32 node, uint64 ttl) external; + + function owner(bytes32 node) external view returns (address); + + function resolver(bytes32 node) external view returns (address); + + function ttl(bytes32 node) external view returns (uint64); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ERC677ReceiverInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ERC677ReceiverInterface.sol new file mode 100644 index 0000000..cea99e2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/ERC677ReceiverInterface.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +interface ERC677ReceiverInterface { + function onTokenTransfer( + address sender, + uint256 amount, + bytes calldata data + ) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol new file mode 100644 index 0000000..83cc7ab --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; + +import "./AggregatorV2V3Interface.sol"; + +interface FeedRegistryInterface { + struct Phase { + uint16 phaseId; + uint80 startingAggregatorRoundId; + uint80 endingAggregatorRoundId; + } + + event FeedProposed( + address indexed asset, + address indexed denomination, + address indexed proposedAggregator, + address currentAggregator, + address sender + ); + event FeedConfirmed( + address indexed asset, + address indexed denomination, + address indexed latestAggregator, + address previousAggregator, + uint16 nextPhaseId, + address sender + ); + + // V3 AggregatorV3Interface + + function decimals(address base, address quote) external view returns (uint8); + + function description(address base, address quote) external view returns (string memory); + + function version(address base, address quote) external view returns (uint256); + + function latestRoundData(address base, address quote) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function getRoundData( + address base, + address quote, + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + // V2 AggregatorInterface + + function latestAnswer(address base, address quote) external view returns (int256 answer); + + function latestTimestamp(address base, address quote) external view returns (uint256 timestamp); + + function latestRound(address base, address quote) external view returns (uint256 roundId); + + function getAnswer( + address base, + address quote, + uint256 roundId + ) external view returns (int256 answer); + + function getTimestamp( + address base, + address quote, + uint256 roundId + ) external view returns (uint256 timestamp); + + // Registry getters + + function getFeed(address base, address quote) external view returns (AggregatorV2V3Interface aggregator); + + function getPhaseFeed( + address base, + address quote, + uint16 phaseId + ) external view returns (AggregatorV2V3Interface aggregator); + + function isFeedEnabled(address aggregator) external view returns (bool); + + function getPhase( + address base, + address quote, + uint16 phaseId + ) external view returns (Phase memory phase); + + // Round helpers + + function getRoundFeed( + address base, + address quote, + uint80 roundId + ) external view returns (AggregatorV2V3Interface aggregator); + + function getPhaseRange( + address base, + address quote, + uint16 phaseId + ) external view returns (uint80 startingRoundId, uint80 endingRoundId); + + function getPreviousRoundId( + address base, + address quote, + uint80 roundId + ) external view returns (uint80 previousRoundId); + + function getNextRoundId( + address base, + address quote, + uint80 roundId + ) external view returns (uint80 nextRoundId); + + // Feed management + + function proposeFeed( + address base, + address quote, + address aggregator + ) external; + + function confirmFeed( + address base, + address quote, + address aggregator + ) external; + + // Proposed aggregator + + function getProposedFeed(address base, address quote) + external + view + returns (AggregatorV2V3Interface proposedAggregator); + + function proposedGetRoundData( + address base, + address quote, + uint80 roundId + ) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function proposedLatestRoundData(address base, address quote) + external + view + returns ( + uint80 id, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + // Phases + function getCurrentPhaseId(address base, address quote) external view returns (uint16 currentPhaseId); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/FlagsInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/FlagsInterface.sol new file mode 100644 index 0000000..9d172c7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/FlagsInterface.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface FlagsInterface { + function getFlag(address) external view returns (bool); + + function getFlags(address[] calldata) external view returns (bool[] memory); + + function raiseFlag(address) external; + + function raiseFlags(address[] calldata) external; + + function lowerFlags(address[] calldata) external; + + function setRaisingAccessController(address) external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol new file mode 100644 index 0000000..d316722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +/** + * @notice This is a deprecated interface. Please use AutomationCompatibleInterface directly. + */ +pragma solidity ^0.8.0; +import {AutomationCompatibleInterface as KeeperCompatibleInterface} from "./AutomationCompatibleInterface.sol"; diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperRegistryInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperRegistryInterface.sol new file mode 100644 index 0000000..3f08060 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperRegistryInterface.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @notice config of the registry + * @dev only used in params and return values + * @member paymentPremiumPPB payment premium rate oracles receive on top of + * being reimbursed for gas, measured in parts per billion + * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, + * priced in MicroLink; can be used in conjunction with or independently of + * paymentPremiumPPB + * @member blockCountPerTurn number of blocks each oracle has during their turn to + * perform upkeep before it will be the next keeper's turn to submit + * @member checkGasLimit gas limit when checking for upkeep + * @member stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling + * @member maxPerformGas max executeGas allowed for an upkeep on this registry + * @member fallbackGasPrice gas price used if the gas price feed is stale + * @member fallbackLinkPrice LINK price used if the LINK price feed is stale + * @member transcoder address of the transcoder contract + * @member registrar address of the registrar contract + */ +struct Config { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK + uint24 blockCountPerTurn; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address registrar; +} + +/** + * @notice config of the registry + * @dev only used in params and return values + * @member nonce used for ID generation + * @ownerLinkBalance withdrawable balance of LINK by contract owner + * @numUpkeeps total number of upkeeps on the registry + */ +struct State { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint256 numUpkeeps; +} + +interface KeeperRegistryBaseInterface { + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + bytes calldata checkData + ) external returns (uint256 id); + + function performUpkeep(uint256 id, bytes calldata performData) external returns (bool success); + + function cancelUpkeep(uint256 id) external; + + function addFunds(uint256 id, uint96 amount) external; + + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; + + function getUpkeep(uint256 id) + external + view + returns ( + address target, + uint32 executeGas, + bytes memory checkData, + uint96 balance, + address lastKeeper, + address admin, + uint64 maxValidBlocknumber, + uint96 amountSpent + ); + + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); + + function getKeeperInfo(address query) + external + view + returns ( + address payee, + bool active, + uint96 balance + ); + + function getState() + external + view + returns ( + State memory, + Config memory, + address[] memory + ); +} + +/** + * @dev The view methods are not actually marked as view in the implementation + * but we want them to be easily queried off-chain. Solidity will not compile + * if we actually inherit from this interface, so we document it here. + */ +interface KeeperRegistryInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + view + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + int256 gasWei, + int256 linkEth + ); +} + +interface KeeperRegistryExecutableInterface is KeeperRegistryBaseInterface { + function checkUpkeep(uint256 upkeepId, address from) + external + returns ( + bytes memory performData, + uint256 maxLinkPayment, + uint256 gasLimit, + uint256 adjustedGasWei, + uint256 linkEth + ); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperRegistryInterface1_2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperRegistryInterface1_2.sol new file mode 100644 index 0000000..124e279 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/KeeperRegistryInterface1_2.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +/** + * @notice This is a deprecated interface. Please use AutomationRegistryInterface1_2 directly. + */ +pragma solidity ^0.8.0; +import {Config, State} from "./AutomationRegistryInterface1_2.sol"; +import {AutomationRegistryBaseInterface as KeeperRegistryBaseInterface} from "./AutomationRegistryInterface1_2.sol"; +import {AutomationRegistryInterface as KeeperRegistryInterface} from "./AutomationRegistryInterface1_2.sol"; +import {AutomationRegistryExecutableInterface as KeeperRegistryExecutableInterface} from "./AutomationRegistryInterface1_2.sol"; diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/LinkTokenInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/LinkTokenInterface.sol new file mode 100644 index 0000000..293d782 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/LinkTokenInterface.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +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); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/MigratableKeeperRegistryInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/MigratableKeeperRegistryInterface.sol new file mode 100644 index 0000000..c93ab69 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/MigratableKeeperRegistryInterface.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../UpkeepFormat.sol"; + +interface MigratableKeeperRegistryInterface { + /** + * @notice Migrates upkeeps from one registry to another, including LINK and upkeep params. + * Only callable by the upkeep admin. All upkeeps must have the same admin. Can only migrate active upkeeps. + * @param upkeepIDs ids of upkeeps to migrate + * @param destination the address of the registry to migrate to + */ + function migrateUpkeeps(uint256[] calldata upkeepIDs, address destination) external; + + /** + * @notice Called by other registries when migrating upkeeps. Only callable by other registries. + * @param encodedUpkeeps abi encoding of upkeeps to import - decoded by the transcoder + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external; + + /** + * @notice Specifies the version of upkeep data that this registry requires in order to import + */ + function upkeepTranscoderVersion() external returns (UpkeepFormat version); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OperatorInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OperatorInterface.sol new file mode 100644 index 0000000..8ad7751 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OperatorInterface.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./OracleInterface.sol"; +import "./ChainlinkRequestInterface.sol"; + +interface OperatorInterface is OracleInterface, ChainlinkRequestInterface { + function operatorRequest( + address sender, + uint256 payment, + bytes32 specId, + bytes4 callbackFunctionId, + uint256 nonce, + uint256 dataVersion, + bytes calldata data + ) external; + + function fulfillOracleRequest2( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes calldata data + ) external returns (bool); + + function ownerTransferAndCall( + address to, + uint256 value, + bytes calldata data + ) external returns (bool success); + + function distributeFunds(address payable[] calldata receivers, uint256[] calldata amounts) external payable; + + function getAuthorizedSenders() external returns (address[] memory); + + function setAuthorizedSenders(address[] calldata senders) external; + + function getForwarder() external returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OracleInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OracleInterface.sol new file mode 100644 index 0000000..c3a9c5e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OracleInterface.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface OracleInterface { + function fulfillOracleRequest( + bytes32 requestId, + uint256 payment, + address callbackAddress, + bytes4 callbackFunctionId, + uint256 expiration, + bytes32 data + ) external returns (bool); + + function isAuthorizedSender(address node) external view returns (bool); + + function withdraw(address recipient, uint256 amount) external; + + function withdrawable() external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OwnableInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OwnableInterface.sol new file mode 100644 index 0000000..a24cbee --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/OwnableInterface.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface OwnableInterface { + function owner() external returns (address); + + function transferOwnership(address recipient) external; + + function acceptOwnership() external; +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/PoRAddressList.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/PoRAddressList.sol new file mode 100644 index 0000000..717c76d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/PoRAddressList.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title Chainlink Proof-of-Reserve address list interface. + * @notice This interface enables Chainlink nodes to get the list addresses to be used in a PoR feed. A single + * contract that implements this interface can only store an address list for a single PoR feed. + * @dev All functions in this interface are expected to be called off-chain, so gas usage is not a big concern. + * This makes it possible to store addresses in optimized data types and convert them to human-readable strings + * in `getPoRAddressList()`. + */ +interface PoRAddressList { + /// @notice Get total number of addresses in the list. + function getPoRAddressListLength() external view returns (uint256); + + /** + * @notice Get a batch of human-readable addresses from the address list. The requested batch size can be greater + * than the actual address list size, in which the full address list will be returned. + * @dev Due to limitations of gas usage in off-chain calls, we need to support fetching the addresses in batches. + * EVM addresses need to be converted to human-readable strings. The address strings need to be in the same format + * that would be used when querying the balance of that address. + * @param startIndex The index of the first address in the batch. + * @param endIndex The index of the last address in the batch. If `endIndex > getPoRAddressListLength()-1`, + * endIndex need to default to `getPoRAddressListLength()-1`. + * @return Array of addresses as strings. + */ + function getPoRAddressList(uint256 startIndex, uint256 endIndex) external view returns (string[] memory); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/PointerInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/PointerInterface.sol new file mode 100644 index 0000000..ca2b82a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/PointerInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface PointerInterface { + function getAddress() external view returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol new file mode 100644 index 0000000..786f275 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +abstract contract TypeAndVersionInterface { + function typeAndVersion() external pure virtual returns (string memory); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/UpkeepTranscoderInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/UpkeepTranscoderInterface.sol new file mode 100644 index 0000000..aa0c3c6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/UpkeepTranscoderInterface.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +import "../UpkeepFormat.sol"; + +pragma solidity ^0.8.0; + +interface UpkeepTranscoderInterface { + function transcodeUpkeeps( + UpkeepFormat fromVersion, + UpkeepFormat toVersion, + bytes calldata encodedUpkeeps + ) external view returns (bytes memory); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol new file mode 100644 index 0000000..d962dc1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface VRFCoordinatorV2Interface { + /** + * @notice Get configuration relevant for making requests + * @return minimumRequestConfirmations global min for request confirmations + * @return maxGasLimit global max for request gas limit + * @return s_provingKeyHashes list of registered key hashes + */ + function getRequestConfig() + external + view + returns ( + uint16, + uint32, + bytes32[] memory + ); + + /** + * @notice Request a set of random words. + * @param keyHash - Corresponds to a particular oracle job which uses + * that key for generating the VRF proof. Different keyHash's have different gas price + * ceilings, so you can select a specific one to bound your maximum per request cost. + * @param subId - The ID of the VRF subscription. Must be funded + * with the minimum subscription balance required for the selected keyHash. + * @param minimumRequestConfirmations - How many blocks you'd like the + * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS + * for why you may want to request more. The acceptable range is + * [minimumRequestBlockConfirmations, 200]. + * @param callbackGasLimit - How much gas you'd like to receive in your + * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords + * may be slightly less than this amount because of gas used calling the function + * (argument decoding etc.), so you may need to request slightly more than you expect + * to have inside fulfillRandomWords. The acceptable range is + * [0, maxGasLimit] + * @param numWords - The number of uint256 random values you'd like to receive + * in your fulfillRandomWords callback. Note these numbers are expanded in a + * secure way by the VRFCoordinator from a single random value supplied by the oracle. + * @return requestId - A unique identifier of the request. Can be used to match + * a request to a response in fulfillRandomWords. + */ + function requestRandomWords( + bytes32 keyHash, + uint64 subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords + ) external returns (uint256 requestId); + + /** + * @notice Create a VRF subscription. + * @return subId - A unique subscription id. + * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. + * @dev Note to fund the subscription, use transferAndCall. For example + * @dev LINKTOKEN.transferAndCall( + * @dev address(COORDINATOR), + * @dev amount, + * @dev abi.encode(subId)); + */ + function createSubscription() external returns (uint64 subId); + + /** + * @notice Get a VRF subscription. + * @param subId - ID of the subscription + * @return balance - LINK balance of the subscription in juels. + * @return reqCount - number of requests for this subscription, determines fee tier. + * @return owner - owner of the subscription. + * @return consumers - list of consumer address which are able to use this subscription. + */ + function getSubscription(uint64 subId) + external + view + returns ( + uint96 balance, + uint64 reqCount, + address owner, + address[] memory consumers + ); + + /** + * @notice Request subscription owner transfer. + * @param subId - ID of the subscription + * @param newOwner - proposed new owner of the subscription + */ + function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external; + + /** + * @notice Request subscription owner transfer. + * @param subId - ID of the subscription + * @dev will revert if original owner of subId has + * not requested that msg.sender become the new owner. + */ + function acceptSubscriptionOwnerTransfer(uint64 subId) external; + + /** + * @notice Add a consumer to a VRF subscription. + * @param subId - ID of the subscription + * @param consumer - New consumer which can use the subscription + */ + function addConsumer(uint64 subId, address consumer) external; + + /** + * @notice Remove a consumer from a VRF subscription. + * @param subId - ID of the subscription + * @param consumer - Consumer to remove from the subscription + */ + function removeConsumer(uint64 subId, address consumer) external; + + /** + * @notice Cancel a subscription + * @param subId - ID of the subscription + * @param to - Where to send the remaining LINK to + */ + function cancelSubscription(uint64 subId, address to) external; + + /* + * @notice Check to see if there exists a request commitment consumers + * for all consumers and keyhashes for a given sub. + * @param subId - ID of the subscription + * @return true if there exists at least one unfulfilled request for the subscription, false + * otherwise. + */ + function pendingRequestExists(uint64 subId) external view returns (bool); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/VRFV2WrapperInterface.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/VRFV2WrapperInterface.sol new file mode 100644 index 0000000..71dbfb6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/VRFV2WrapperInterface.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface VRFV2WrapperInterface { + /** + * @return the request ID of the most recent VRF V2 request made by this wrapper. This should only + * be relied option within the same transaction that the request was made. + */ + function lastRequestId() external view returns (uint256); + + /** + * @notice Calculates the price of a VRF request with the given callbackGasLimit at the current + * @notice block. + * + * @dev This function relies on the transaction gas price which is not automatically set during + * @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function. + * + * @param _callbackGasLimit is the gas limit used to estimate the price. + */ + function calculateRequestPrice(uint32 _callbackGasLimit) external view returns (uint256); + + /** + * @notice Estimates the price of a VRF request with a specific gas limit and gas price. + * + * @dev This is a convenience function that can be called in simulation to better understand + * @dev pricing. + * + * @param _callbackGasLimit is the gas limit used to estimate the price. + * @param _requestGasPriceWei is the gas price in wei used for the estimation. + */ + function estimateRequestPrice(uint32 _callbackGasLimit, uint256 _requestGasPriceWei) external view returns (uint256); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/libraries/external/Cron.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/libraries/external/Cron.sol new file mode 100644 index 0000000..0c93c19 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/libraries/external/Cron.sol @@ -0,0 +1,78 @@ +pragma solidity 0.8.6; + +import {Cron as CronInternal, Spec} from "../internal/Cron.sol"; + +/** + * @title The Cron library + * @notice A utility contract for encoding/decoding cron strings (ex: 0 0 * * *) into an + * abstraction called a Spec. The library also includes a spec function, nextTick(), which + * determines the next time a cron job should fire based on the current block timestamp. + * @dev this is the external version of the library, which relies on the internal library + * by the same name. + */ +library Cron { + using CronInternal for Spec; + using CronInternal for string; + + /** + * @notice nextTick calculates the next datetime that a spec "ticks", starting + * from the current block timestamp. This is gas-intensive and therefore should + * only be called off-chain. + * @param spec the spec to evaluate + * @return the next tick + */ + function nextTick(Spec calldata spec) public view returns (uint256) { + return spec.nextTick(); + } + + /** + * @notice lastTick calculates the previous datetime that a spec "ticks", starting + * from the current block timestamp. This is gas-intensive and therefore should + * only be called off-chain. + * @param spec the spec to evaluate + * @return the next tick + */ + function lastTick(Spec calldata spec) public view returns (uint256) { + return spec.lastTick(); + } + + /** + * @notice matches evaluates whether or not a spec "ticks" at a given timestamp + * @param spec the spec to evaluate + * @param timestamp the timestamp to compare against + * @return true / false if they match + */ + function matches(Spec calldata spec, uint256 timestamp) public view returns (bool) { + return spec.matches(timestamp); + } + + /** + * @notice toSpec converts a cron string to a spec struct. This is gas-intensive + * and therefore should only be called off-chain. + * @param cronString the cron string + * @return the spec struct + */ + function toSpec(string calldata cronString) public pure returns (Spec memory) { + return cronString.toSpec(); + } + + /** + * @notice toEncodedSpec converts a cron string to an abi-encoded spec. This is gas-intensive + * and therefore should only be called off-chain. + * @param cronString the cron string + * @return the abi-encoded spec + */ + function toEncodedSpec(string calldata cronString) public pure returns (bytes memory) { + return cronString.toEncodedSpec(); + } + + /** + * @notice toCronString converts a cron spec to a human-readable cron string. This is gas-intensive + * and therefore should only be called off-chain. + * @param spec the cron spec + * @return the corresponding cron string + */ + function toCronString(Spec calldata spec) public pure returns (string memory) { + return spec.toCronString(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/libraries/internal/Cron.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/libraries/internal/Cron.sol new file mode 100644 index 0000000..81e113c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/libraries/internal/Cron.sol @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: MIT + +/* + The Cron contract serves two primary functions: + * parsing cron-formatted strings like "0 0 * * *" into + structs called "Specs" + * computing the "next tick" of a cron spec + + Because manipulating strings is gas-expensive in solidity, + the intended use of this contract is for users to first convert + their cron strings to encoded Spec structs via toEncodedSpec(). + Then, the user stores the Spec on chain. Finally, users use the nextTick(), + function to determine the datetime of the next cron job run. + + Cron jobs are interpreted according to this format: + + ┌───────────── minute (0 - 59) + │ ┌───────────── hour (0 - 23) + │ │ ┌───────────── day of the month (1 - 31) + │ │ │ ┌───────────── month (1 - 12) + │ │ │ │ ┌───────────── day of the week (0 - 6) (Monday to Sunday) + │ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ + * * * * * + + Special limitations: + * there is no year field + * no special characters: ? L W # + * lists can have a max length of 26 + * no words like JAN / FEB or MON / TUES +*/ + +pragma solidity 0.8.6; + +import "../../vendor/Strings.sol"; +import "../../vendor/DateTime.sol"; + +// The fields of a cron spec, by name +string constant MINUTE = "minute"; +string constant HOUR = "hour"; +string constant DAY = "day"; +string constant MONTH = "month"; +string constant DAY_OF_WEEK = "day of week"; + +error UnknownFieldType(); +error InvalidSpec(string reason); +error InvalidField(string field, string reason); +error ListTooLarge(); + +// Set of enums representing a cron field type +enum FieldType { + WILD, + EXACT, + INTERVAL, + RANGE, + LIST +} + +// A spec represents a cron job by decomposing it into 5 fields +struct Spec { + Field minute; + Field hour; + Field day; + Field month; + Field dayOfWeek; +} + +// A field represents a single element in a cron spec. There are 5 types +// of fields (see above). Not all properties of this struct are present at once. +struct Field { + FieldType fieldType; + uint8 singleValue; + uint8 interval; + uint8 rangeStart; + uint8 rangeEnd; + uint8 listLength; + uint8[26] list; +} + +/** + * @title The Cron library + * @notice A utility contract for encoding/decoding cron strings (ex: 0 0 * * *) into an + * abstraction called a Spec. The library also includes a spec function, nextTick(), which + * determines the next time a cron job should fire based on the current block timestamp. + */ +library Cron { + using strings for *; + + /** + * @notice nextTick calculates the next datetime that a spec "ticks", starting + * from the current block timestamp. This is gas-intensive and therefore should + * only be called off-chain. + * @param spec the spec to evaluate + * @return the next tick + * @dev this is the internal version of the library. There is also an external version. + */ + function nextTick(Spec memory spec) internal view returns (uint256) { + uint16 year = DateTime.getYear(block.timestamp); + uint8 month = DateTime.getMonth(block.timestamp); + uint8 day = DateTime.getDay(block.timestamp); + uint8 hour = DateTime.getHour(block.timestamp); + uint8 minute = DateTime.getMinute(block.timestamp); + uint8 dayOfWeek; + for (; true; year++) { + for (; month <= 12; month++) { + if (!matches(spec.month, month)) { + day = 1; + hour = 0; + minute = 0; + continue; + } + uint8 maxDay = DateTime.getDaysInMonth(month, year); + for (; day <= maxDay; day++) { + if (!matches(spec.day, day)) { + hour = 0; + minute = 0; + continue; + } + dayOfWeek = DateTime.getWeekday(DateTime.toTimestamp(year, month, day)); + if (!matches(spec.dayOfWeek, dayOfWeek)) { + hour = 0; + minute = 0; + continue; + } + for (; hour < 24; hour++) { + if (!matches(spec.hour, hour)) { + minute = 0; + continue; + } + for (; minute < 60; minute++) { + if (!matches(spec.minute, minute)) { + continue; + } + return DateTime.toTimestamp(year, month, day, hour, minute); + } + minute = 0; + } + hour = 0; + } + day = 1; + } + month = 1; + } + } + + /** + * @notice lastTick calculates the previous datetime that a spec "ticks", starting + * from the current block timestamp. This is gas-intensive and therefore should + * only be called off-chain. + * @param spec the spec to evaluate + * @return the next tick + */ + function lastTick(Spec memory spec) internal view returns (uint256) { + uint16 year = DateTime.getYear(block.timestamp); + uint8 month = DateTime.getMonth(block.timestamp); + uint8 day = DateTime.getDay(block.timestamp); + uint8 hour = DateTime.getHour(block.timestamp); + uint8 minute = DateTime.getMinute(block.timestamp); + uint8 dayOfWeek; + bool resetDay; + for (; true; year--) { + for (; month > 0; month--) { + if (!matches(spec.month, month)) { + resetDay = true; + hour = 23; + minute = 59; + continue; + } + if (resetDay) { + day = DateTime.getDaysInMonth(month, year); + } + for (; day > 0; day--) { + if (!matches(spec.day, day)) { + hour = 23; + minute = 59; + continue; + } + dayOfWeek = DateTime.getWeekday(DateTime.toTimestamp(year, month, day)); + if (!matches(spec.dayOfWeek, dayOfWeek)) { + hour = 23; + minute = 59; + continue; + } + for (; hour >= 0; hour--) { + if (!matches(spec.hour, hour)) { + minute = 59; + if (hour == 0) { + break; + } + continue; + } + for (; minute >= 0; minute--) { + if (!matches(spec.minute, minute)) { + if (minute == 0) { + break; + } + continue; + } + return DateTime.toTimestamp(year, month, day, hour, minute); + } + minute = 59; + if (hour == 0) { + break; + } + } + hour = 23; + } + resetDay = true; + } + month = 12; + } + } + + /** + * @notice matches evaluates whether or not a spec "ticks" at a given timestamp + * @param spec the spec to evaluate + * @param timestamp the timestamp to compare against + * @return true / false if they match + */ + function matches(Spec memory spec, uint256 timestamp) internal view returns (bool) { + DateTime._DateTime memory dt = DateTime.parseTimestamp(timestamp); + return + matches(spec.month, dt.month) && + matches(spec.day, dt.day) && + matches(spec.hour, dt.hour) && + matches(spec.minute, dt.minute); + } + + /** + * @notice toSpec converts a cron string to a spec struct. This is gas-intensive + * and therefore should only be called off-chain. + * @param cronString the cron string + * @return the spec struct + */ + function toSpec(string memory cronString) internal pure returns (Spec memory) { + strings.slice memory space = strings.toSlice(" "); + strings.slice memory cronSlice = strings.toSlice(cronString); + if (cronSlice.count(space) != 4) { + revert InvalidSpec("4 spaces required"); + } + strings.slice memory minuteSlice = cronSlice.split(space); + strings.slice memory hourSlice = cronSlice.split(space); + strings.slice memory daySlice = cronSlice.split(space); + strings.slice memory monthSlice = cronSlice.split(space); + // DEV: dayOfWeekSlice = cronSlice + // The cronSlice now contains the last section of the cron job, + // which corresponds to the day of week + if ( + minuteSlice.len() == 0 || + hourSlice.len() == 0 || + daySlice.len() == 0 || + monthSlice.len() == 0 || + cronSlice.len() == 0 + ) { + revert InvalidSpec("some fields missing"); + } + return + validate( + Spec({ + minute: sliceToField(minuteSlice), + hour: sliceToField(hourSlice), + day: sliceToField(daySlice), + month: sliceToField(monthSlice), + dayOfWeek: sliceToField(cronSlice) + }) + ); + } + + /** + * @notice toEncodedSpec converts a cron string to an abi-encoded spec. This is gas-intensive + * and therefore should only be called off-chain. + * @param cronString the cron string + * @return the abi-encoded spec + */ + function toEncodedSpec(string memory cronString) internal pure returns (bytes memory) { + return abi.encode(toSpec(cronString)); + } + + /** + * @notice toCronString converts a cron spec to a human-readable cron string. This is gas-intensive + * and therefore should only be called off-chain. + * @param spec the cron spec + * @return the corresponding cron string + */ + function toCronString(Spec memory spec) internal pure returns (string memory) { + return + string( + bytes.concat( + fieldToBstring(spec.minute), + " ", + fieldToBstring(spec.hour), + " ", + fieldToBstring(spec.day), + " ", + fieldToBstring(spec.month), + " ", + fieldToBstring(spec.dayOfWeek) + ) + ); + } + + /** + * @notice matches evaluates if a values matches a field. + * ex: 3 matches *, 3 matches 0-5, 3 does not match 0,2,4 + * @param field the field struct to match against + * @param value the value of a field + * @return true / false if they match + */ + function matches(Field memory field, uint8 value) private pure returns (bool) { + if (field.fieldType == FieldType.WILD) { + return true; + } else if (field.fieldType == FieldType.INTERVAL) { + return value % field.interval == 0; + } else if (field.fieldType == FieldType.EXACT) { + return value == field.singleValue; + } else if (field.fieldType == FieldType.RANGE) { + return value >= field.rangeStart && value <= field.rangeEnd; + } else if (field.fieldType == FieldType.LIST) { + for (uint256 idx = 0; idx < field.listLength; idx++) { + if (value == field.list[idx]) { + return true; + } + } + return false; + } + revert UnknownFieldType(); + } + + // VALIDATIONS + + /** + * @notice validate validates a spec, reverting if any errors are found + * @param spec the spec to validate + * @return the original spec + */ + function validate(Spec memory spec) private pure returns (Spec memory) { + validateField(spec.dayOfWeek, DAY_OF_WEEK, 0, 6); + validateField(spec.month, MONTH, 1, 12); + uint8 maxDay = maxDayForMonthField(spec.month); + validateField(spec.day, DAY, 1, maxDay); + validateField(spec.hour, HOUR, 0, 23); + validateField(spec.minute, MINUTE, 0, 59); + return spec; + } + + /** + * @notice validateField validates the value of a field. It reverts if an error is found. + * @param field the field to validate + * @param fieldName the name of the field ex "minute" or "hour" + * @param min the minimum value a field can have (usually 1 or 0) + * @param max the maximum value a field can have (ex minute = 59, hour = 23) + */ + function validateField( + Field memory field, + string memory fieldName, + uint8 min, + uint8 max + ) private pure { + if (field.fieldType == FieldType.WILD) { + return; + } else if (field.fieldType == FieldType.EXACT) { + if (field.singleValue < min || field.singleValue > max) { + string memory reason = string( + bytes.concat("value must be >=,", uintToBString(min), " and <=", uintToBString(max)) + ); + revert InvalidField(fieldName, reason); + } + } else if (field.fieldType == FieldType.INTERVAL) { + if (field.interval < 1 || field.interval > max) { + string memory reason = string( + bytes.concat("inverval must be */(", uintToBString(1), "-", uintToBString(max), ")") + ); + revert InvalidField(fieldName, reason); + } + } else if (field.fieldType == FieldType.RANGE) { + if (field.rangeEnd > max || field.rangeEnd <= field.rangeStart) { + string memory reason = string( + bytes.concat("inverval must be within ", uintToBString(min), "-", uintToBString(max)) + ); + revert InvalidField(fieldName, reason); + } + } else if (field.fieldType == FieldType.LIST) { + if (field.listLength < 2) { + revert InvalidField(fieldName, "lists must have at least 2 items"); + } + string memory reason = string( + bytes.concat("items in list must be within ", uintToBString(min), "-", uintToBString(max)) + ); + uint8 listItem; + for (uint256 idx = 0; idx < field.listLength; idx++) { + listItem = field.list[idx]; + if (listItem < min || listItem > max) { + revert InvalidField(fieldName, reason); + } + } + } else { + revert UnknownFieldType(); + } + } + + /** + * @notice maxDayForMonthField returns the maximum valid day given the month field + * @param month the month field + * @return the max day + */ + function maxDayForMonthField(Field memory month) private pure returns (uint8) { + // DEV: ranges are always safe because any two consecutive months will always + // contain a month with 31 days + if (month.fieldType == FieldType.WILD || month.fieldType == FieldType.RANGE) { + return 31; + } else if (month.fieldType == FieldType.EXACT) { + // DEV: assume leap year in order to get max value + return DateTime.getDaysInMonth(month.singleValue, 4); + } else if (month.fieldType == FieldType.INTERVAL) { + if (month.interval == 9 || month.interval == 11) { + return 30; + } else { + return 31; + } + } else if (month.fieldType == FieldType.LIST) { + uint8 result; + for (uint256 idx = 0; idx < month.listLength; idx++) { + // DEV: assume leap year in order to get max value + uint8 daysInMonth = DateTime.getDaysInMonth(month.list[idx], 4); + if (daysInMonth == 31) { + return daysInMonth; + } + if (daysInMonth > result) { + result = daysInMonth; + } + } + return result; + } else { + revert UnknownFieldType(); + } + } + + /** + * @notice sliceToField converts a strings.slice to a field struct + * @param fieldSlice the slice of a string representing the field of a cron job + * @return the field + */ + function sliceToField(strings.slice memory fieldSlice) private pure returns (Field memory) { + strings.slice memory star = strings.toSlice("*"); + strings.slice memory dash = strings.toSlice("-"); + strings.slice memory slash = strings.toSlice("/"); + strings.slice memory comma = strings.toSlice(","); + Field memory field; + if (fieldSlice.equals(star)) { + field.fieldType = FieldType.WILD; + } else if (fieldSlice.contains(dash)) { + field.fieldType = FieldType.RANGE; + strings.slice memory start = fieldSlice.split(dash); + field.rangeStart = sliceToUint8(start); + field.rangeEnd = sliceToUint8(fieldSlice); + } else if (fieldSlice.contains(slash)) { + field.fieldType = FieldType.INTERVAL; + fieldSlice.split(slash); + field.interval = sliceToUint8(fieldSlice); + } else if (fieldSlice.contains(comma)) { + field.fieldType = FieldType.LIST; + strings.slice memory token; + while (fieldSlice.len() > 0) { + if (field.listLength > 25) { + revert ListTooLarge(); + } + token = fieldSlice.split(comma); + field.list[field.listLength] = sliceToUint8(token); + field.listLength++; + } + } else { + // needs input validation + field.fieldType = FieldType.EXACT; + field.singleValue = sliceToUint8(fieldSlice); + } + return field; + } + + /** + * @notice fieldToBstring converts a field to the bytes representation of that field string + * @param field the field to stringify + * @return bytes representing the string, ex: bytes("*") + */ + function fieldToBstring(Field memory field) private pure returns (bytes memory) { + if (field.fieldType == FieldType.WILD) { + return "*"; + } else if (field.fieldType == FieldType.EXACT) { + return uintToBString(uint256(field.singleValue)); + } else if (field.fieldType == FieldType.RANGE) { + return bytes.concat(uintToBString(field.rangeStart), "-", uintToBString(field.rangeEnd)); + } else if (field.fieldType == FieldType.INTERVAL) { + return bytes.concat("*/", uintToBString(uint256(field.interval))); + } else if (field.fieldType == FieldType.LIST) { + bytes memory result = uintToBString(field.list[0]); + for (uint256 idx = 1; idx < field.listLength; idx++) { + result = bytes.concat(result, ",", uintToBString(field.list[idx])); + } + return result; + } + revert UnknownFieldType(); + } + + /** + * @notice uintToBString converts a uint256 to a bytes representation of that uint as a string + * @param n the number to stringify + * @return bytes representing the string, ex: bytes("1") + */ + function uintToBString(uint256 n) private pure returns (bytes memory) { + if (n == 0) { + return "0"; + } + uint256 j = n; + uint256 len; + while (j != 0) { + len++; + j /= 10; + } + bytes memory bstr = new bytes(len); + uint256 k = len; + while (n != 0) { + k = k - 1; + uint8 temp = (48 + uint8(n - (n / 10) * 10)); + bytes1 b1 = bytes1(temp); + bstr[k] = b1; + n /= 10; + } + return bstr; + } + + /** + * @notice sliceToUint8 converts a strings.slice to uint8 + * @param slice the string slice to convert to a uint8 + * @return the number that the string represents ex: "20" --> 20 + */ + function sliceToUint8(strings.slice memory slice) private pure returns (uint8) { + bytes memory b = bytes(slice.toString()); + uint8 i; + uint8 result = 0; + for (i = 0; i < b.length; i++) { + uint8 c = uint8(b[i]); + if (c >= 48 && c <= 57) { + result = result * 10 + (c - 48); + } + } + return result; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/MockAggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/MockAggregator.sol new file mode 100644 index 0000000..cf6cba8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/MockAggregator.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract MockAggregator { + int256 public s_answer; + + function setLatestAnswer(int256 answer) public { + s_answer = answer; + } + + function latestAnswer() public view returns (int256) { + return s_answer; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/MockAggregatorValidator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/MockAggregatorValidator.sol new file mode 100644 index 0000000..a43236d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/MockAggregatorValidator.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/AggregatorValidatorInterface.sol"; + +contract MockAggregatorValidator is AggregatorValidatorInterface { + uint8 immutable id; + + constructor(uint8 id_) { + id = id_; + } + + event ValidateCalled( + uint8 id, + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ); + + function validate( + uint256 previousRoundId, + int256 previousAnswer, + uint256 currentRoundId, + int256 currentAnswer + ) external override returns (bool) { + emit ValidateCalled(id, previousRoundId, previousAnswer, currentRoundId, currentAnswer); + return true; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/VRFCoordinatorMock.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/VRFCoordinatorMock.sol new file mode 100644 index 0000000..21b9875 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/VRFCoordinatorMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../VRFConsumerBase.sol"; + +contract VRFCoordinatorMock { + LinkTokenInterface public LINK; + + event RandomnessRequest(address indexed sender, bytes32 indexed keyHash, uint256 indexed seed); + + constructor(address linkAddress) public { + LINK = LinkTokenInterface(linkAddress); + } + + function onTokenTransfer( + address sender, + uint256 fee, + bytes memory _data + ) public onlyLINK { + (bytes32 keyHash, uint256 seed) = abi.decode(_data, (bytes32, uint256)); + emit RandomnessRequest(sender, keyHash, seed); + } + + function callBackWithRandomness( + bytes32 requestId, + uint256 randomness, + address consumerContract + ) public { + VRFConsumerBase v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomness.selector, requestId, randomness); + uint256 b = 206000; + require(gasleft() >= b, "not enough gas for consumer"); + (bool success, ) = consumerContract.call(resp); + } + + modifier onlyLINK() { + require(msg.sender == address(LINK), "Must use LINK token"); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol new file mode 100644 index 0000000..1a366c2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: MIT +// A mock for testing code that relies on VRFCoordinatorV2. +pragma solidity ^0.8.4; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; + +contract VRFCoordinatorV2Mock is VRFCoordinatorV2Interface { + uint96 public immutable BASE_FEE; + uint96 public immutable GAS_PRICE_LINK; + uint16 public immutable MAX_CONSUMERS = 100; + + error InvalidSubscription(); + error InsufficientBalance(); + error MustBeSubOwner(address owner); + error TooManyConsumers(); + error InvalidConsumer(); + error InvalidRandomWords(); + + event RandomWordsRequested( + bytes32 indexed keyHash, + uint256 requestId, + uint256 preSeed, + uint64 indexed subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + address indexed sender + ); + event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success); + event SubscriptionCreated(uint64 indexed subId, address owner); + event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); + event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); + event ConsumerAdded(uint64 indexed subId, address consumer); + event ConsumerRemoved(uint64 indexed subId, address consumer); + + uint64 s_currentSubId; + uint256 s_nextRequestId = 1; + uint256 s_nextPreSeed = 100; + struct Subscription { + address owner; + uint96 balance; + } + mapping(uint64 => Subscription) s_subscriptions; /* subId */ /* subscription */ + mapping(uint64 => address[]) s_consumers; /* subId */ /* consumers */ + + struct Request { + uint64 subId; + uint32 callbackGasLimit; + uint32 numWords; + } + mapping(uint256 => Request) s_requests; /* requestId */ /* request */ + + constructor(uint96 _baseFee, uint96 _gasPriceLink) { + BASE_FEE = _baseFee; + GAS_PRICE_LINK = _gasPriceLink; + } + + function consumerIsAdded(uint64 _subId, address _consumer) public view returns (bool) { + address[] memory consumers = s_consumers[_subId]; + for (uint256 i = 0; i < consumers.length; i++) { + if (consumers[i] == _consumer) { + return true; + } + } + return false; + } + + modifier onlyValidConsumer(uint64 _subId, address _consumer) { + if (!consumerIsAdded(_subId, _consumer)) { + revert InvalidConsumer(); + } + _; + } + + /** + * @notice fulfillRandomWords fulfills the given request, sending the random words to the supplied + * @notice consumer. + * + * @dev This mock uses a simplified formula for calculating payment amount and gas usage, and does + * @dev not account for all edge cases handled in the real VRF coordinator. When making requests + * @dev against the real coordinator a small amount of additional LINK is required. + * + * @param _requestId the request to fulfill + * @param _consumer the VRF randomness consumer to send the result to + */ + function fulfillRandomWords(uint256 _requestId, address _consumer) external { + fulfillRandomWordsWithOverride(_requestId, _consumer, new uint256[](0)); + } + + /** + * @notice fulfillRandomWordsWithOverride allows the user to pass in their own random words. + * + * @param _requestId the request to fulfill + * @param _consumer the VRF randomness consumer to send the result to + * @param _words user-provided random words + */ + function fulfillRandomWordsWithOverride( + uint256 _requestId, + address _consumer, + uint256[] memory _words + ) public { + uint256 startGas = gasleft(); + if (s_requests[_requestId].subId == 0) { + revert("nonexistent request"); + } + Request memory req = s_requests[_requestId]; + + if (_words.length == 0) { + _words = new uint256[](req.numWords); + for (uint256 i = 0; i < req.numWords; i++) { + _words[i] = uint256(keccak256(abi.encode(_requestId, i))); + } + } else if (_words.length != req.numWords) { + revert InvalidRandomWords(); + } + + VRFConsumerBaseV2 v; + bytes memory callReq = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, _requestId, _words); + (bool success, ) = _consumer.call{gas: req.callbackGasLimit}(callReq); + + uint96 payment = uint96(BASE_FEE + ((startGas - gasleft()) * GAS_PRICE_LINK)); + if (s_subscriptions[req.subId].balance < payment) { + revert InsufficientBalance(); + } + s_subscriptions[req.subId].balance -= payment; + delete (s_requests[_requestId]); + emit RandomWordsFulfilled(_requestId, _requestId, payment, success); + } + + /** + * @notice fundSubscription allows funding a subscription with an arbitrary amount for testing. + * + * @param _subId the subscription to fund + * @param _amount the amount to fund + */ + function fundSubscription(uint64 _subId, uint96 _amount) public { + if (s_subscriptions[_subId].owner == address(0)) { + revert InvalidSubscription(); + } + uint96 oldBalance = s_subscriptions[_subId].balance; + s_subscriptions[_subId].balance += _amount; + emit SubscriptionFunded(_subId, oldBalance, oldBalance + _amount); + } + + function requestRandomWords( + bytes32 _keyHash, + uint64 _subId, + uint16 _minimumRequestConfirmations, + uint32 _callbackGasLimit, + uint32 _numWords + ) external override onlyValidConsumer(_subId, msg.sender) returns (uint256) { + if (s_subscriptions[_subId].owner == address(0)) { + revert InvalidSubscription(); + } + + uint256 requestId = s_nextRequestId++; + uint256 preSeed = s_nextPreSeed++; + + s_requests[requestId] = Request({subId: _subId, callbackGasLimit: _callbackGasLimit, numWords: _numWords}); + + emit RandomWordsRequested( + _keyHash, + requestId, + preSeed, + _subId, + _minimumRequestConfirmations, + _callbackGasLimit, + _numWords, + msg.sender + ); + return requestId; + } + + function createSubscription() external override returns (uint64 _subId) { + s_currentSubId++; + s_subscriptions[s_currentSubId] = Subscription({owner: msg.sender, balance: 0}); + emit SubscriptionCreated(s_currentSubId, msg.sender); + return s_currentSubId; + } + + function getSubscription(uint64 _subId) + external + view + override + returns ( + uint96 balance, + uint64 reqCount, + address owner, + address[] memory consumers + ) + { + if (s_subscriptions[_subId].owner == address(0)) { + revert InvalidSubscription(); + } + return (s_subscriptions[_subId].balance, 0, s_subscriptions[_subId].owner, s_consumers[_subId]); + } + + function cancelSubscription(uint64 _subId, address _to) external override onlySubOwner(_subId) { + emit SubscriptionCanceled(_subId, _to, s_subscriptions[_subId].balance); + delete (s_subscriptions[_subId]); + } + + modifier onlySubOwner(uint64 _subId) { + address owner = s_subscriptions[_subId].owner; + if (owner == address(0)) { + revert InvalidSubscription(); + } + if (msg.sender != owner) { + revert MustBeSubOwner(owner); + } + _; + } + + function getRequestConfig() + external + pure + override + returns ( + uint16, + uint32, + bytes32[] memory + ) + { + return (3, 2000000, new bytes32[](0)); + } + + function addConsumer(uint64 _subId, address _consumer) external override onlySubOwner(_subId) { + if (s_consumers[_subId].length == MAX_CONSUMERS) { + revert TooManyConsumers(); + } + + if (consumerIsAdded(_subId, _consumer)) { + return; + } + + s_consumers[_subId].push(_consumer); + emit ConsumerAdded(_subId, _consumer); + } + + function removeConsumer(uint64 _subId, address _consumer) + external + override + onlySubOwner(_subId) + onlyValidConsumer(_subId, _consumer) + { + address[] storage consumers = s_consumers[_subId]; + for (uint256 i = 0; i < consumers.length; i++) { + if (consumers[i] == _consumer) { + address last = consumers[consumers.length - 1]; + consumers[i] = last; + consumers.pop(); + break; + } + } + + emit ConsumerRemoved(_subId, _consumer); + } + + function getConfig() + external + view + returns ( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation + ) + { + return (4, 2_500_000, 2_700, 33285); + } + + function getFeeConfig() + external + view + returns ( + uint32 fulfillmentFlatFeeLinkPPMTier1, + uint32 fulfillmentFlatFeeLinkPPMTier2, + uint32 fulfillmentFlatFeeLinkPPMTier3, + uint32 fulfillmentFlatFeeLinkPPMTier4, + uint32 fulfillmentFlatFeeLinkPPMTier5, + uint24 reqsForTier2, + uint24 reqsForTier3, + uint24 reqsForTier4, + uint24 reqsForTier5 + ) + { + return ( + 100000, // 0.1 LINK + 100000, // 0.1 LINK + 100000, // 0.1 LINK + 100000, // 0.1 LINK + 100000, // 0.1 LINK + 0, + 0, + 0, + 0 + ); + } + + function getFallbackWeiPerUnitLink() external view returns (int256) { + return 4000000000000000; // 0.004 Ether + } + + function requestSubscriptionOwnerTransfer(uint64 _subId, address _newOwner) external pure override { + revert("not implemented"); + } + + function acceptSubscriptionOwnerTransfer(uint64 _subId) external pure override { + revert("not implemented"); + } + + function pendingRequestExists(uint64 subId) public view override returns (bool) { + revert("not implemented"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Broken.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Broken.sol new file mode 100644 index 0000000..21fa9b0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Broken.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.8.0; + +// Broken is a contract to aid debugging and testing reverting calls during development. +contract Broken { + error Unauthorized(string reason, int256 reason2); + + function revertWithCustomError() public pure { + revert Unauthorized("param", 121); + } + + function revertWithMessage(string memory message) public pure { + require(false, message); + } + + function revertSilently() public pure { + require(false); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol new file mode 100644 index 0000000..09712c2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ChainlinkClientTestHelper.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../ChainlinkClient.sol"; + +contract ChainlinkClientTestHelper is ChainlinkClient { + constructor(address _link, address _oracle) { + setChainlinkToken(_link); + setChainlinkOracle(_oracle); + } + + event Request(bytes32 id, address callbackAddress, bytes4 callbackfunctionSelector, bytes data); + event LinkAmount(uint256 amount); + + function publicNewRequest( + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature + ) public { + Chainlink.Request memory req = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + emit Request(req.id, req.callbackAddress, req.callbackFunctionId, req.buf.buf); + } + + function publicRequest( + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory req = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + sendChainlinkRequest(req, _wei); + } + + function publicRequestRunTo( + address _oracle, + bytes32 _id, + address _address, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory run = buildChainlinkRequest(_id, _address, bytes4(keccak256(_fulfillmentSignature))); + sendChainlinkRequestTo(_oracle, run, _wei); + } + + function publicRequestOracleData( + bytes32 _id, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory req = buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature))); + sendOperatorRequest(req, _wei); + } + + function publicRequestOracleDataFrom( + address _oracle, + bytes32 _id, + bytes memory _fulfillmentSignature, + uint256 _wei + ) public { + Chainlink.Request memory run = buildOperatorRequest(_id, bytes4(keccak256(_fulfillmentSignature))); + sendOperatorRequestTo(_oracle, run, _wei); + } + + function publicCancelRequest( + bytes32 _requestId, + uint256 _payment, + bytes4 _callbackFunctionId, + uint256 _expiration + ) public { + cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration); + } + + function publicChainlinkToken() public view returns (address) { + return chainlinkTokenAddress(); + } + + function publicFulfillChainlinkRequest(bytes32 _requestId, bytes32) public { + fulfillRequest(_requestId, bytes32(0)); + } + + function fulfillRequest(bytes32 _requestId, bytes32) public { + validateChainlinkCallback(_requestId); + } + + function publicLINK(uint256 _amount) public { + emit LinkAmount(LINK_DIVISIBILITY * _amount); + } + + function publicOracleAddress() public view returns (address) { + return chainlinkOracleAddress(); + } + + function publicAddExternalRequest(address _oracle, bytes32 _requestId) public { + addChainlinkExternalRequest(_oracle, _requestId); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ChainlinkTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ChainlinkTestHelper.sol new file mode 100644 index 0000000..1ca38f3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ChainlinkTestHelper.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../Chainlink.sol"; +import "../vendor/CBORChainlink.sol"; +import "../vendor/BufferChainlink.sol"; + +contract ChainlinkTestHelper { + using Chainlink for Chainlink.Request; + using CBORChainlink for BufferChainlink.buffer; + + Chainlink.Request private req; + + event RequestData(bytes payload); + + function closeEvent() public { + emit RequestData(req.buf.buf); + } + + function setBuffer(bytes memory data) public { + Chainlink.Request memory r2 = req; + r2.setBuffer(data); + req = r2; + } + + function add(string memory _key, string memory _value) public { + Chainlink.Request memory r2 = req; + r2.add(_key, _value); + req = r2; + } + + function addBytes(string memory _key, bytes memory _value) public { + Chainlink.Request memory r2 = req; + r2.addBytes(_key, _value); + req = r2; + } + + function addInt(string memory _key, int256 _value) public { + Chainlink.Request memory r2 = req; + r2.addInt(_key, _value); + req = r2; + } + + function addUint(string memory _key, uint256 _value) public { + Chainlink.Request memory r2 = req; + r2.addUint(_key, _value); + req = r2; + } + + // Temporarily have method receive bytes32[] memory until experimental + // string[] memory can be invoked from truffle tests. + function addStringArray(string memory _key, string[] memory _values) public { + Chainlink.Request memory r2 = req; + r2.addStringArray(_key, _values); + req = r2; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Counter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Counter.sol new file mode 100644 index 0000000..1ceb789 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Counter.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract Counter { + error AlwaysRevert(); + + uint256 public count = 0; + + function increment() public returns (uint256) { + count += 1; + return count; + } + + function reset() public { + count = 0; + } + + function alwaysRevert() public pure { + revert AlwaysRevert(); + } + + function alwaysRevertWithString() public pure { + revert("always revert"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronReceiver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronReceiver.sol new file mode 100644 index 0000000..cee50c1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronReceiver.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +contract CronReceiver { + event Received1(); + event Received2(); + + function handler1() external { + emit Received1(); + } + + function handler2() external { + emit Received2(); + } + + function revertHandler() external { + revert("revert!"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronTestHelper.sol new file mode 100644 index 0000000..f500547 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronTestHelper.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import {Cron as CronInternal, Spec} from "../libraries/internal/Cron.sol"; +import {Cron as CronExternal} from "../libraries/external/Cron.sol"; + +/** + * @title The CronInternalTestHelper contract + * @notice This contract exposes core functionality of the internal/Cron library. + * It is only intended for use in tests. + */ +contract CronInternalTestHelper { + /** + * @notice Converts a cron string to a Spec, validates the spec, and encodes the spec. + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to convert and encode + * @return the abi encoding of the Spec struct representing the cron string + */ + function encodeCronString(string memory cronString) external pure returns (bytes memory) { + return CronInternal.toEncodedSpec(cronString); + } + + /** + * @notice encodedSpecToString is a helper function for turning an + * encoded spec back into a string. There is limited or no use for this outside + * of tests. + */ + function encodedSpecToString(bytes memory encodedSpec) public pure returns (string memory) { + Spec memory spec = abi.decode(encodedSpec, (Spec)); + return CronInternal.toCronString(spec); + } + + /** + * @notice calculateNextTick calculates the next time a cron job should "tick". + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to consider + * @return the timestamp in UTC of the next "tick" + */ + function calculateNextTick(string memory cronString) external view returns (uint256) { + return CronInternal.nextTick(CronInternal.toSpec(cronString)); + } + + /** + * @notice calculateLastTick calculates the last time a cron job "ticked". + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to consider + * @return the timestamp in UTC of the last "tick" + */ + function calculateLastTick(string memory cronString) external view returns (uint256) { + return CronInternal.lastTick(CronInternal.toSpec(cronString)); + } +} + +/** + * @title The CronExternalTestHelper contract + * @notice This contract exposes core functionality of the external/Cron library. + * It is only intended for use in tests. + */ +contract CronExternalTestHelper { + /** + * @notice Converts a cron string to a Spec, validates the spec, and encodes the spec. + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to convert and encode + * @return the abi encoding of the Spec struct representing the cron string + */ + function encodeCronString(string memory cronString) external pure returns (bytes memory) { + return CronExternal.toEncodedSpec(cronString); + } + + /** + * @notice encodedSpecToString is a helper function for turning an + * encoded spec back into a string. There is limited or no use for this outside + * of tests. + */ + function encodedSpecToString(bytes memory encodedSpec) public pure returns (string memory) { + Spec memory spec = abi.decode(encodedSpec, (Spec)); + return CronExternal.toCronString(spec); + } + + /** + * @notice calculateNextTick calculates the next time a cron job should "tick". + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to consider + * @return the timestamp in UTC of the next "tick" + */ + function calculateNextTick(string memory cronString) external view returns (uint256) { + return CronExternal.nextTick(CronExternal.toSpec(cronString)); + } + + /** + * @notice calculateLastTick calculates the last time a cron job "ticked". + * This should only be called off-chain, as it is gas expensive! + * @param cronString the cron string to consider + * @return the timestamp in UTC of the last "tick" + */ + function calculateLastTick(string memory cronString) external view returns (uint256) { + return CronExternal.lastTick(CronExternal.toSpec(cronString)); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronUpkeepTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronUpkeepTestHelper.sol new file mode 100644 index 0000000..9406d10 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/CronUpkeepTestHelper.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import "../upkeeps/CronUpkeep.sol"; +import {Cron, Spec} from "../libraries/internal/Cron.sol"; + +/** + * @title The CronUpkeepTestHelper contract + * @notice This contract exposes core functionality of the CronUpkeep contract. + * It is only intended for use in tests. + */ +contract CronUpkeepTestHelper is CronUpkeep { + using Cron for Spec; + using Cron for string; + + constructor( + address owner, + address delegate, + uint256 maxJobs, + bytes memory firstJob + ) CronUpkeep(owner, delegate, maxJobs, firstJob) {} + + /** + * @notice createCronJobFromString is a helper function for creating cron jobs + * directly from strings. This is gas-intensive and shouldn't be done outside + * of testing environments. + */ + function createCronJobFromString( + address target, + bytes memory handler, + string memory cronString + ) external { + Spec memory spec = cronString.toSpec(); + createCronJobFromSpec(target, handler, spec); + } + + /** + * @notice txCheckUpkeep is a helper function for sending real txs to the + * checkUpkeep function. This allows us to do gas analysis on it. + */ + function txCheckUpkeep(bytes calldata checkData) external { + address(this).call(abi.encodeWithSelector(bytes4(keccak256("checkUpkeep(bytes)")), checkData)); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/EthBalanceMonitorExposed.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/EthBalanceMonitorExposed.sol new file mode 100644 index 0000000..06f5a84 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/EthBalanceMonitorExposed.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import "../upkeeps/EthBalanceMonitor.sol"; + +contract EthBalanceMonitorExposed is EthBalanceMonitor { + constructor(address keeperRegistryAddress, uint256 minWaitPeriod) + EthBalanceMonitor(keeperRegistryAddress, minWaitPeriod) + {} + + function setLastTopUpXXXTestOnly(address target, uint56 lastTopUpTimestamp) external { + s_targets[target].lastTopUpTimestamp = lastTopUpTimestamp; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/FeedConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/FeedConsumer.sol new file mode 100644 index 0000000..9960433 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/FeedConsumer.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol"; + +contract FeedConsumer { + AggregatorV2V3Interface public immutable AGGREGATOR; + + constructor(address feedAddress) { + AGGREGATOR = AggregatorV2V3Interface(feedAddress); + } + + function latestAnswer() external view returns (int256 answer) { + return AGGREGATOR.latestAnswer(); + } + + function latestTimestamp() external view returns (uint256) { + return AGGREGATOR.latestTimestamp(); + } + + function latestRound() external view returns (uint256) { + return AGGREGATOR.latestRound(); + } + + function getAnswer(uint256 roundId) external view returns (int256) { + return AGGREGATOR.getAnswer(roundId); + } + + function getTimestamp(uint256 roundId) external view returns (uint256) { + return AGGREGATOR.getTimestamp(roundId); + } + + function decimals() external view returns (uint8) { + return AGGREGATOR.decimals(); + } + + function description() external view returns (string memory) { + return AGGREGATOR.description(); + } + + function version() external view returns (uint256) { + return AGGREGATOR.version(); + } + + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return AGGREGATOR.getRoundData(_roundId); + } + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return AGGREGATOR.latestRoundData(); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/FlagsTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/FlagsTestHelper.sol new file mode 100644 index 0000000..3e35cae --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/FlagsTestHelper.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../Flags.sol"; + +contract FlagsTestHelper { + Flags public flags; + + constructor(address flagsContract) { + flags = Flags(flagsContract); + } + + function getFlag(address subject) external view returns (bool) { + return flags.getFlag(subject); + } + + function getFlags(address[] calldata subjects) external view returns (bool[] memory) { + return flags.getFlags(subjects); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Greeter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Greeter.sol new file mode 100644 index 0000000..cb10fa1 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/Greeter.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.8.0; + +import "../ConfirmedOwner.sol"; + +contract Greeter is ConfirmedOwner { + string public greeting; + + constructor(address owner) ConfirmedOwner(owner) {} + + function setGreeting(string calldata _greeting) external onlyOwner { + require(bytes(_greeting).length > 0, "Invalid greeting length"); + greeting = _greeting; + } + + function triggerRevert() external pure { + require(false, "Greeter: revert triggered"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/KeeperCompatibleTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/KeeperCompatibleTestHelper.sol new file mode 100644 index 0000000..51c0bc7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/KeeperCompatibleTestHelper.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../KeeperCompatible.sol"; + +contract KeeperCompatibleTestHelper is KeeperCompatible { + function checkUpkeep(bytes calldata) external override returns (bool, bytes memory) {} + + function performUpkeep(bytes calldata) external override {} + + function testCannotExecute() public view cannotExecute {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/LogEmitter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/LogEmitter.sol new file mode 100644 index 0000000..4d7b979 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/LogEmitter.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.8.0; + +contract LogEmitter { + event Log1(uint256); + event Log2(uint256 indexed); + event Log3(string); + + function EmitLog1(uint256[] memory v) public { + for (uint256 i = 0; i < v.length; i++) { + emit Log1(v[i]); + } + } + + function EmitLog2(uint256[] memory v) public { + for (uint256 i = 0; i < v.length; i++) { + emit Log2(v[i]); + } + } + + function EmitLog3(string[] memory v) public { + for (uint256 i = 0; i < v.length; i++) { + emit Log3(v[i]); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockArbGasInfo.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockArbGasInfo.sol new file mode 100644 index 0000000..d88f052 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockArbGasInfo.sol @@ -0,0 +1,7 @@ +pragma solidity 0.8.6; + +contract MockArbGasInfo { + function getCurrentTxL1GasFees() external view returns (uint256) { + return 1000000; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockArbitrumInbox.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockArbitrumInbox.sol new file mode 100644 index 0000000..0c90d19 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockArbitrumInbox.sol @@ -0,0 +1,122 @@ +import {IInbox} from "../dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol"; +import {IBridge} from "../dev/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IBridge.sol"; + +contract MockArbitrumInbox is IInbox { + event RetryableTicketNoRefundAliasRewriteCreated( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes data + ); + + function sendL2Message(bytes calldata messageData) external override returns (uint256) { + return 0; + } + + function sendUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + uint256 amount, + bytes calldata data + ) external override returns (uint256) { + return 0; + } + + function sendContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + uint256 amount, + bytes calldata data + ) external override returns (uint256) { + return 0; + } + + function sendL1FundedUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + bytes calldata data + ) external payable override returns (uint256) { + return 0; + } + + function sendL1FundedContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + bytes calldata data + ) external payable override returns (uint256) { + return 0; + } + + function createRetryableTicketNoRefundAliasRewrite( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable override returns (uint256) { + emit RetryableTicketNoRefundAliasRewriteCreated( + destAddr, + arbTxCallValue, + maxSubmissionCost, + submissionRefundAddress, + valueRefundAddress, + maxGas, + gasPriceBid, + data + ); + return 42; + } + + function createRetryableTicket( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable override returns (uint256) { + return 0; + } + + function depositEth(address destAddr) external payable override returns (uint256) { + return 0; + } + + function depositEthRetryable( + address destAddr, + uint256 maxSubmissionCost, + uint256 maxGas, + uint256 maxGasPrice + ) external payable override returns (uint256) { + return 0; + } + + function bridge() external view override returns (IBridge) { + return IBridge(address(0)); + } + + /// @notice This mock function simply replicates the formula used by Arbitrum's + /// DelayedInbox in the Nitro upgrade. The function has been copied here from the Arbitrum + /// team's repository. + /// @param dataLength The length of the calldata that will be executed in L2 + /// @param baseFee The base fee to pay for the transaction. + /// @dev The calculation will use the L1 base fee if it is passed 0. + function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) public view returns (uint256) { + return (1400 + 6 * dataLength) * (baseFee == 0 ? block.basefee : baseFee); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol new file mode 100644 index 0000000..29790b0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol @@ -0,0 +1,7 @@ +pragma solidity 0.8.6; + +contract MockOVMGasPriceOracle { + function getL1Fee(bytes memory _data) public view returns (uint256) { + return 2000000; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol new file mode 100644 index 0000000..d9dc164 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOptimismL1CrossDomainMessenger.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +/* Interface Imports */ +import {IL1CrossDomainMessenger} from "@eth-optimism/contracts/L1/messaging/IL1CrossDomainMessenger.sol"; + +contract MockOptimismL1CrossDomainMessenger is IL1CrossDomainMessenger { + uint256 private s_nonce; + + // slither-disable-next-line external-function + function xDomainMessageSender() public view returns (address) { + return address(0); + } + + function sendMessage( + address _target, + bytes memory _message, + uint32 _gasLimit + ) public { + emit SentMessage(_target, msg.sender, _message, s_nonce, _gasLimit); + s_nonce++; + } + + /** + * Relays a cross domain message to a contract. + * @inheritdoc IL1CrossDomainMessenger + */ + // slither-disable-next-line external-function + function relayMessage( + address _target, + address _sender, + bytes memory _message, + uint256 _messageNonce, + L2MessageInclusionProof memory _proof + ) public {} + + function replayMessage( + address _target, + address _sender, + bytes memory _message, + uint256 _queueIndex, + uint32 _oldGasLimit, + uint32 _newGasLimit + ) public {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOptimismL2CrossDomainMessenger.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOptimismL2CrossDomainMessenger.sol new file mode 100644 index 0000000..8845a0d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockOptimismL2CrossDomainMessenger.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +/* Interface Imports */ +import {IL2CrossDomainMessenger} from "@eth-optimism/contracts/L2/messaging/IL2CrossDomainMessenger.sol"; + +contract MockOptimismL2CrossDomainMessenger is IL2CrossDomainMessenger { + uint256 private s_nonce; + address private s_sender; + + // slither-disable-next-line external-function + function xDomainMessageSender() public view returns (address) { + return s_sender; + } + + function setSender(address newSender) external { + s_sender = newSender; + } + + function sendMessage( + address _target, + bytes memory _message, + uint32 _gasLimit + ) public { + emit SentMessage(_target, msg.sender, _message, s_nonce, _gasLimit); + s_nonce++; + } + + function relayMessage( + address _target, + address _sender, + bytes memory _message, + uint256 _messageNonce + ) external {} + + receive() external payable {} +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockV3Aggregator.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockV3Aggregator.sol new file mode 100644 index 0000000..01951b8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/MockV3Aggregator.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/AggregatorV2V3Interface.sol"; + +/** + * @title MockV3Aggregator + * @notice Based on the FluxAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV3Aggregator is AggregatorV2V3Interface { + uint256 public constant override version = 0; + + uint8 public override decimals; + int256 public override latestAnswer; + uint256 public override latestTimestamp; + uint256 public override latestRound; + + mapping(uint256 => int256) public override getAnswer; + mapping(uint256 => uint256) public override getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor(uint8 _decimals, int256 _initialAnswer) { + decimals = _decimals; + updateAnswer(_initialAnswer); + } + + function updateAnswer(int256 _answer) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + getStartedAt[latestRound] = block.timestamp; + } + + function updateRoundData( + uint80 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } + + function getRoundData(uint80 _roundId) + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return (_roundId, getAnswer[_roundId], getStartedAt[_roundId], getTimestamp[_roundId], _roundId); + } + + function latestRoundData() + external + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + uint80(latestRound), + getAnswer[latestRound], + getStartedAt[latestRound], + getTimestamp[latestRound], + uint80(latestRound) + ); + } + + function description() external pure override returns (string memory) { + return "v0.8/tests/MockV3Aggregator.sol"; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveEmitter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveEmitter.sol new file mode 100644 index 0000000..641bf75 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveEmitter.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +contract ReceiveEmitter { + event FundsReceived(uint256 amount, uint256 newBalance); + + receive() external payable { + emit FundsReceived(msg.value, address(this).balance); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveFallbackEmitter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveFallbackEmitter.sol new file mode 100644 index 0000000..618787e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveFallbackEmitter.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +contract ReceiveFallbackEmitter { + event FundsReceived(uint256 amount, uint256 newBalance); + + fallback() external payable { + emit FundsReceived(msg.value, address(this).balance); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveReverter.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveReverter.sol new file mode 100644 index 0000000..d9a2bdc --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/ReceiveReverter.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +contract ReceiveReverter { + receive() external payable { + revert("Can't send funds"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumer.sol new file mode 100644 index 0000000..70c4ea0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumer.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../VRFConsumerBase.sol"; + +contract VRFConsumer is VRFConsumerBase { + uint256 public randomnessOutput; + bytes32 public requestId; + + constructor(address vrfCoordinator, address link) + // solhint-disable-next-line no-empty-blocks + VRFConsumerBase(vrfCoordinator, link) + { + /* empty */ + } + + function fulfillRandomness( + bytes32, /* requestId */ + uint256 randomness + ) internal override { + randomnessOutput = randomness; + requestId = requestId; + } + + function testRequestRandomness(bytes32 keyHash, uint256 fee) external returns (bytes32) { + return requestRandomness(keyHash, fee); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumerExternalSubOwnerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumerExternalSubOwnerExample.sol new file mode 100644 index 0000000..a449ce8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumerExternalSubOwnerExample.sol @@ -0,0 +1,74 @@ +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../dev/VRFConsumerBaseV2.sol"; + +contract VRFConsumerExternalSubOwnerExample is VRFConsumerBaseV2 { + + VRFCoordinatorV2Interface COORDINATOR; + LinkTokenInterface LINKTOKEN; + + struct RequestConfig { + uint64 subId; + uint32 callbackGasLimit; + uint16 requestConfirmations; + uint32 numWords; + bytes32 keyHash; + } + RequestConfig s_requestConfig; + uint256[] s_randomWords; + uint256 s_requestId; + + constructor( + address vrfCoordinator, + address link, + uint32 callbackGasLimit, + uint16 requestConfirmations, + uint32 numWords, + bytes32 keyHash + ) + VRFConsumerBaseV2(vrfCoordinator) + { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + LINKTOKEN = LinkTokenInterface(link); + s_requestConfig = RequestConfig({ + subId: 0, // Initially unset + callbackGasLimit: callbackGasLimit, + requestConfirmations: requestConfirmations, + numWords: numWords, + keyHash: keyHash + }); + } + + function fulfillRandomWords( + uint256 requestId, + uint256[] memory randomWords + ) + internal + override + { + s_randomWords = randomWords; + } + + function requestRandomWords() + external + { + RequestConfig memory rc = s_requestConfig; + // Will revert if subscription is not set and funded. + s_requestId = COORDINATOR.requestRandomWords( + rc.keyHash, + rc.subId, + rc.requestConfirmations, + rc.callbackGasLimit, + rc.numWords); + } + + function setSubscriptionID( + uint64 subId + ) + public + { + s_requestConfig.subId = subId; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumerV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumerV2.sol new file mode 100644 index 0000000..3a97e89 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFConsumerV2.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; + +contract VRFConsumerV2 is VRFConsumerBaseV2 { + uint256[] public s_randomWords; + uint256 public s_requestId; + VRFCoordinatorV2Interface COORDINATOR; + LinkTokenInterface LINKTOKEN; + uint64 public s_subId; + uint256 public s_gasAvailable; + + constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + LINKTOKEN = LinkTokenInterface(link); + } + + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { + require(requestId == s_requestId, "request ID is incorrect"); + + s_gasAvailable = gasleft(); + s_randomWords = randomWords; + } + + function testCreateSubscriptionAndFund(uint96 amount) external { + if (s_subId == 0) { + s_subId = COORDINATOR.createSubscription(); + COORDINATOR.addConsumer(s_subId, address(this)); + } + // Approve the link transfer. + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); + } + + function topUpSubscription(uint96 amount) external { + require(s_subId != 0, "sub not set"); + // Approve the link transfer. + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); + } + + function updateSubscription(address[] memory consumers) external { + require(s_subId != 0, "subID not set"); + for (uint256 i = 0; i < consumers.length; i++) { + COORDINATOR.addConsumer(s_subId, consumers[i]); + } + } + + function testRequestRandomness( + bytes32 keyHash, + uint64 subId, + uint16 minReqConfs, + uint32 callbackGasLimit, + uint32 numWords + ) external returns (uint256) { + s_requestId = COORDINATOR.requestRandomWords(keyHash, subId, minReqConfs, callbackGasLimit, numWords); + return s_requestId; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFCoordinatorV2TestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFCoordinatorV2TestHelper.sol new file mode 100644 index 0000000..d6afe92 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFCoordinatorV2TestHelper.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../VRFCoordinatorV2.sol"; + +contract VRFCoordinatorV2TestHelper is VRFCoordinatorV2 { + uint96 s_paymentAmount; + uint256 s_gasStart; + + constructor( + address link, + address blockhashStore, + address linkEthFeed + ) + // solhint-disable-next-line no-empty-blocks + VRFCoordinatorV2(link, blockhashStore, linkEthFeed) + { + /* empty */ + } + + function calculatePaymentAmountTest( + uint256 gasAfterPaymentCalculation, + uint32 fulfillmentFlatFeeLinkPPM, + uint256 weiPerUnitGas + ) external { + s_paymentAmount = calculatePaymentAmount( + gasleft(), + gasAfterPaymentCalculation, + fulfillmentFlatFeeLinkPPM, + weiPerUnitGas + ); + } + + function getPaymentAmount() public view returns (uint96) { + return s_paymentAmount; + } + + function getGasStart() public view returns (uint256) { + return s_gasStart; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFExternalSubOwnerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFExternalSubOwnerExample.sol new file mode 100644 index 0000000..fcd7722 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFExternalSubOwnerExample.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; + +contract VRFExternalSubOwnerExample is VRFConsumerBaseV2 { + VRFCoordinatorV2Interface COORDINATOR; + LinkTokenInterface LINKTOKEN; + + uint256[] public s_randomWords; + uint256 public s_requestId; + address s_owner; + + constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + LINKTOKEN = LinkTokenInterface(link); + s_owner = msg.sender; + } + + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { + require(requestId == s_requestId, "request ID is incorrect"); + s_randomWords = randomWords; + } + + function requestRandomWords( + uint64 subId, + uint32 callbackGasLimit, + uint16 requestConfirmations, + uint32 numWords, + bytes32 keyHash + ) external onlyOwner { + // Will revert if subscription is not funded. + s_requestId = COORDINATOR.requestRandomWords(keyHash, subId, requestConfirmations, callbackGasLimit, numWords); + } + + function transferOwnership(address newOwner) external onlyOwner { + s_owner = newOwner; + } + + modifier onlyOwner() { + require(msg.sender == s_owner); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFLoadTestExternalSubOwner.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFLoadTestExternalSubOwner.sol new file mode 100644 index 0000000..ee60b37 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFLoadTestExternalSubOwner.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; +import "../ConfirmedOwner.sol"; + +/** + * @title The VRFLoadTestExternalSubOwner contract. + * @notice Allows making many VRF V2 randomness requests in a single transaction for load testing. + */ +contract VRFLoadTestExternalSubOwner is VRFConsumerBaseV2, ConfirmedOwner { + VRFCoordinatorV2Interface public immutable COORDINATOR; + LinkTokenInterface public immutable LINK; + + uint256 public s_responseCount; + + constructor(address _vrfCoordinator, address _link) VRFConsumerBaseV2(_vrfCoordinator) ConfirmedOwner(msg.sender) { + COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator); + LINK = LinkTokenInterface(_link); + } + + function fulfillRandomWords(uint256, uint256[] memory) internal override { + s_responseCount++; + } + + function requestRandomWords( + uint64 _subId, + uint16 _requestConfirmations, + bytes32 _keyHash, + uint16 _requestCount + ) external onlyOwner { + for (uint16 i = 0; i < _requestCount; i++) { + COORDINATOR.requestRandomWords(_keyHash, _subId, _requestConfirmations, 50_000, 1); + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFLoadTestOwnerlessConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFLoadTestOwnerlessConsumer.sol new file mode 100644 index 0000000..47cd824 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFLoadTestOwnerlessConsumer.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../VRFConsumerBase.sol"; +import "../interfaces/ERC677ReceiverInterface.sol"; + +/** + * @title The VRFLoadTestOwnerlessConsumer contract. + * @notice Allows making many VRF V1 randomness requests in a single transaction for load testing. + */ +contract VRFLoadTestOwnerlessConsumer is VRFConsumerBase, ERC677ReceiverInterface { + // The price of each VRF request in Juels. 1 LINK = 1e18 Juels. + uint256 public immutable PRICE; + + uint256 public s_responseCount; + + constructor( + address _vrfCoordinator, + address _link, + uint256 _price + ) VRFConsumerBase(_vrfCoordinator, _link) { + PRICE = _price; + } + + function fulfillRandomness(bytes32, uint256) internal override { + s_responseCount++; + } + + /** + * @dev Creates as many randomness requests as can be made with the funds transferred. + * @param _amount The amount of LINK transferred to pay for these requests. + * @param _data The data passed to transferAndCall on LinkToken. Must be an abi-encoded key hash. + */ + function onTokenTransfer( + address, + uint256 _amount, + bytes calldata _data + ) external override { + if (msg.sender != address(LINK)) { + revert("only callable from LINK"); + } + bytes32 keyHash = abi.decode(_data, (bytes32)); + + uint256 spent = 0; + while (spent + PRICE <= _amount) { + requestRandomness(keyHash, PRICE); + spent += PRICE; + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFMaliciousConsumerV2.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFMaliciousConsumerV2.sol new file mode 100644 index 0000000..86e6e4f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFMaliciousConsumerV2.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; + +contract VRFMaliciousConsumerV2 is VRFConsumerBaseV2 { + uint256[] public s_randomWords; + uint256 public s_requestId; + VRFCoordinatorV2Interface COORDINATOR; + LinkTokenInterface LINKTOKEN; + uint64 public s_subId; + uint256 public s_gasAvailable; + bytes32 s_keyHash; + + constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + LINKTOKEN = LinkTokenInterface(link); + } + + function setKeyHash(bytes32 keyHash) public { + s_keyHash = keyHash; + } + + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { + s_gasAvailable = gasleft(); + s_randomWords = randomWords; + s_requestId = requestId; + // Should revert + COORDINATOR.requestRandomWords(s_keyHash, s_subId, 1, 200000, 1); + } + + function testCreateSubscriptionAndFund(uint96 amount) external { + if (s_subId == 0) { + s_subId = COORDINATOR.createSubscription(); + COORDINATOR.addConsumer(s_subId, address(this)); + } + // Approve the link transfer. + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); + } + + function updateSubscription(address[] memory consumers) external { + require(s_subId != 0, "subID not set"); + for (uint256 i = 0; i < consumers.length; i++) { + COORDINATOR.addConsumer(s_subId, consumers[i]); + } + } + + function testRequestRandomness() external returns (uint256) { + return COORDINATOR.requestRandomWords(s_keyHash, s_subId, 1, 500000, 1); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFOwnerlessConsumerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFOwnerlessConsumerExample.sol new file mode 100644 index 0000000..299241a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFOwnerlessConsumerExample.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// An example VRF V1 consumer contract that can be triggered using a transferAndCall from the link +// contract. +pragma solidity ^0.8.4; + +import "../VRFConsumerBase.sol"; +import "../interfaces/ERC677ReceiverInterface.sol"; + +contract VRFOwnerlessConsumerExample is VRFConsumerBase, ERC677ReceiverInterface { + uint256 public s_randomnessOutput; + bytes32 public s_requestId; + + error OnlyCallableFromLink(); + + constructor(address _vrfCoordinator, address _link) VRFConsumerBase(_vrfCoordinator, _link) { + /* empty */ + } + + function fulfillRandomness(bytes32 requestId, uint256 _randomness) internal override { + require(requestId == s_requestId, "request ID is incorrect"); + s_randomnessOutput = _randomness; + } + + /** + * @dev Creates a new randomness request. This function can only be used by calling + * transferAndCall on the LinkToken contract. + * @param _amount The amount of LINK transferred to pay for this request. + * @param _data The data passed to transferAndCall on LinkToken. Must be an abi-encoded key hash. + */ + function onTokenTransfer( + address, /* sender */ + uint256 _amount, + bytes calldata _data + ) external override { + if (msg.sender != address(LINK)) { + revert OnlyCallableFromLink(); + } + + bytes32 keyHash = abi.decode(_data, (bytes32)); + s_requestId = requestRandomness(keyHash, _amount); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFRequestIDBaseTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFRequestIDBaseTestHelper.sol new file mode 100644 index 0000000..b97a835 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFRequestIDBaseTestHelper.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../VRFRequestIDBase.sol"; + +contract VRFRequestIDBaseTestHelper is VRFRequestIDBase { + function makeVRFInputSeed_( + bytes32 _keyHash, + uint256 _userSeed, + address _requester, + uint256 _nonce + ) public pure returns (uint256) { + return makeVRFInputSeed(_keyHash, _userSeed, _requester, _nonce); + } + + function makeRequestId_(bytes32 _keyHash, uint256 _vRFInputSeed) public pure returns (bytes32) { + return makeRequestId(_keyHash, _vRFInputSeed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFSingleConsumerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFSingleConsumerExample.sol new file mode 100644 index 0000000..19b9640 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFSingleConsumerExample.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +// Example of a single consumer contract which owns the subscription. +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; + +contract VRFSingleConsumerExample is VRFConsumerBaseV2 { + VRFCoordinatorV2Interface COORDINATOR; + LinkTokenInterface LINKTOKEN; + + struct RequestConfig { + uint64 subId; + uint32 callbackGasLimit; + uint16 requestConfirmations; + uint32 numWords; + bytes32 keyHash; + } + RequestConfig public s_requestConfig; + uint256[] public s_randomWords; + uint256 public s_requestId; + address s_owner; + + constructor( + address vrfCoordinator, + address link, + uint32 callbackGasLimit, + uint16 requestConfirmations, + uint32 numWords, + bytes32 keyHash + ) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + LINKTOKEN = LinkTokenInterface(link); + s_owner = msg.sender; + s_requestConfig = RequestConfig({ + subId: 0, // Unset initially + callbackGasLimit: callbackGasLimit, + requestConfirmations: requestConfirmations, + numWords: numWords, + keyHash: keyHash + }); + subscribe(); + } + + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { + require(requestId == s_requestId, "request ID is incorrect"); + s_randomWords = randomWords; + } + + // Assumes the subscription is funded sufficiently. + function requestRandomWords() external onlyOwner { + RequestConfig memory rc = s_requestConfig; + // Will revert if subscription is not set and funded. + s_requestId = COORDINATOR.requestRandomWords( + rc.keyHash, + rc.subId, + rc.requestConfirmations, + rc.callbackGasLimit, + rc.numWords + ); + } + + // Assumes this contract owns link + // This method is analogous to VRFv1, except the amount + // should be selected based on the keyHash (each keyHash functions like a "gas lane" + // with different link costs). + function fundAndRequestRandomWords(uint256 amount) external onlyOwner { + RequestConfig memory rc = s_requestConfig; + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_requestConfig.subId)); + // Will revert if subscription is not set and funded. + s_requestId = COORDINATOR.requestRandomWords( + rc.keyHash, + rc.subId, + rc.requestConfirmations, + rc.callbackGasLimit, + rc.numWords + ); + } + + // Assumes this contract owns link + function topUpSubscription(uint256 amount) external onlyOwner { + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_requestConfig.subId)); + } + + function withdraw(uint256 amount, address to) external onlyOwner { + LINKTOKEN.transfer(to, amount); + } + + function unsubscribe(address to) external onlyOwner { + // Returns funds to this address + COORDINATOR.cancelSubscription(s_requestConfig.subId, to); + s_requestConfig.subId = 0; + } + + // Keep this separate in case the contract want to unsubscribe and then + // resubscribe. + function subscribe() public onlyOwner { + // Create a subscription, current subId + address[] memory consumers = new address[](1); + consumers[0] = address(this); + s_requestConfig.subId = COORDINATOR.createSubscription(); + COORDINATOR.addConsumer(s_requestConfig.subId, consumers[0]); + } + + modifier onlyOwner() { + require(msg.sender == s_owner); + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFTestHelper.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFTestHelper.sol new file mode 100644 index 0000000..3a6d68f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFTestHelper.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../VRF.sol"; + +/** *********************************************************************** + @notice Testing harness for VRF.sol, exposing its internal methods. Not to + @notice be used for production. +*/ +contract VRFTestHelper is VRF { + function bigModExp_(uint256 base, uint256 exponent) public view returns (uint256) { + return super.bigModExp(base, exponent); + } + + function squareRoot_(uint256 x) public view returns (uint256) { + return super.squareRoot(x); + } + + function ySquared_(uint256 x) public pure returns (uint256) { + return super.ySquared(x); + } + + function fieldHash_(bytes memory b) public pure returns (uint256) { + return super.fieldHash(b); + } + + function hashToCurve_(uint256[2] memory pk, uint256 x) public view returns (uint256[2] memory) { + return super.hashToCurve(pk, x); + } + + function ecmulVerify_( + uint256[2] memory x, + uint256 scalar, + uint256[2] memory q + ) public pure returns (bool) { + return super.ecmulVerify(x, scalar, q); + } + + function projectiveECAdd_( + uint256 px, + uint256 py, + uint256 qx, + uint256 qy + ) + public + pure + returns ( + uint256, + uint256, + uint256 + ) + { + return super.projectiveECAdd(px, py, qx, qy); + } + + function affineECAdd_( + uint256[2] memory p1, + uint256[2] memory p2, + uint256 invZ + ) public pure returns (uint256[2] memory) { + return super.affineECAdd(p1, p2, invZ); + } + + function verifyLinearCombinationWithGenerator_( + uint256 c, + uint256[2] memory p, + uint256 s, + address lcWitness + ) public pure returns (bool) { + return super.verifyLinearCombinationWithGenerator(c, p, s, lcWitness); + } + + function linearCombination_( + uint256 c, + uint256[2] memory p1, + uint256[2] memory cp1Witness, + uint256 s, + uint256[2] memory p2, + uint256[2] memory sp2Witness, + uint256 zInv + ) public pure returns (uint256[2] memory) { + return super.linearCombination(c, p1, cp1Witness, s, p2, sp2Witness, zInv); + } + + function scalarFromCurvePoints_( + uint256[2] memory hash, + uint256[2] memory pk, + uint256[2] memory gamma, + address uWitness, + uint256[2] memory v + ) public pure returns (uint256) { + return super.scalarFromCurvePoints(hash, pk, gamma, uWitness, v); + } + + function isOnCurve_(uint256[2] memory p) public pure returns (bool) { + return super.isOnCurve(p); + } + + function verifyVRFProof_( + uint256[2] memory pk, + uint256[2] memory gamma, + uint256 c, + uint256 s, + uint256 seed, + address uWitness, + uint256[2] memory cGammaWitness, + uint256[2] memory sHashWitness, + uint256 zInv + ) public view { + super.verifyVRFProof(pk, gamma, c, s, seed, uWitness, cGammaWitness, sHashWitness, zInv); + } + + function randomValueFromVRFProof_(Proof memory proof, uint256 seed) public view returns (uint256 output) { + return super.randomValueFromVRFProof(proof, seed); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2RevertingExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2RevertingExample.sol new file mode 100644 index 0000000..86f7bcb --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2RevertingExample.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFCoordinatorV2Interface.sol"; +import "../VRFConsumerBaseV2.sol"; + +// VRFV2RevertingExample will always revert. Used for testing only, useless in prod. +contract VRFV2RevertingExample is VRFConsumerBaseV2 { + uint256[] public s_randomWords; + uint256 public s_requestId; + VRFCoordinatorV2Interface COORDINATOR; + LinkTokenInterface LINKTOKEN; + uint64 public s_subId; + uint256 public s_gasAvailable; + + constructor(address vrfCoordinator, address link) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + LINKTOKEN = LinkTokenInterface(link); + } + + function fulfillRandomWords(uint256, uint256[] memory) internal override { + revert(); + } + + function testCreateSubscriptionAndFund(uint96 amount) external { + if (s_subId == 0) { + s_subId = COORDINATOR.createSubscription(); + COORDINATOR.addConsumer(s_subId, address(this)); + } + // Approve the link transfer. + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); + } + + function topUpSubscription(uint96 amount) external { + require(s_subId != 0, "sub not set"); + // Approve the link transfer. + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); + } + + function updateSubscription(address[] memory consumers) external { + require(s_subId != 0, "subID not set"); + for (uint256 i = 0; i < consumers.length; i++) { + COORDINATOR.addConsumer(s_subId, consumers[i]); + } + } + + function testRequestRandomness( + bytes32 keyHash, + uint64 subId, + uint16 minReqConfs, + uint32 callbackGasLimit, + uint32 numWords + ) external returns (uint256) { + s_requestId = COORDINATOR.requestRandomWords(keyHash, subId, minReqConfs, callbackGasLimit, numWords); + return s_requestId; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperConsumerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperConsumerExample.sol new file mode 100644 index 0000000..b35a1e7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperConsumerExample.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "../VRFV2WrapperConsumerBase.sol"; +import "../ConfirmedOwner.sol"; + +contract VRFV2WrapperConsumerExample is VRFV2WrapperConsumerBase, ConfirmedOwner { + event WrappedRequestFulfilled(uint256 requestId, uint256[] randomWords, uint256 payment); + event WrapperRequestMade(uint256 indexed requestId, uint256 paid); + + struct RequestStatus { + uint256 paid; + bool fulfilled; + uint256[] randomWords; + } + mapping(uint256 => RequestStatus) /* requestId */ /* requestStatus */ + public s_requests; + + constructor(address _link, address _vrfV2Wrapper) + ConfirmedOwner(msg.sender) + VRFV2WrapperConsumerBase(_link, _vrfV2Wrapper) + {} + + function makeRequest( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords + ) external onlyOwner returns (uint256 requestId) { + requestId = requestRandomness(_callbackGasLimit, _requestConfirmations, _numWords); + uint256 paid = VRF_V2_WRAPPER.calculateRequestPrice(_callbackGasLimit); + s_requests[requestId] = RequestStatus({paid: paid, randomWords: new uint256[](0), fulfilled: false}); + emit WrapperRequestMade(requestId, paid); + return requestId; + } + + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override { + require(s_requests[_requestId].paid > 0, "request not found"); + s_requests[_requestId].fulfilled = true; + s_requests[_requestId].randomWords = _randomWords; + emit WrappedRequestFulfilled(_requestId, _randomWords, s_requests[_requestId].paid); + } + + function getRequestStatus(uint256 _requestId) + external + view + returns ( + uint256 paid, + bool fulfilled, + uint256[] memory randomWords + ) + { + require(s_requests[_requestId].paid > 0, "request not found"); + RequestStatus memory request = s_requests[_requestId]; + return (request.paid, request.fulfilled, request.randomWords); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperOutOfGasConsumerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperOutOfGasConsumerExample.sol new file mode 100644 index 0000000..b586acf --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperOutOfGasConsumerExample.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "../VRFV2WrapperConsumerBase.sol"; +import "../ConfirmedOwner.sol"; + +contract VRFV2WrapperOutOfGasConsumerExample is VRFV2WrapperConsumerBase, ConfirmedOwner { + constructor(address _link, address _vrfV2Wrapper) + ConfirmedOwner(msg.sender) + VRFV2WrapperConsumerBase(_link, _vrfV2Wrapper) + {} + + function makeRequest( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords + ) external onlyOwner returns (uint256 requestId) { + return requestRandomness(_callbackGasLimit, _requestConfirmations, _numWords); + } + + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal view override { + while (gasleft() > 0) {} + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperRevertingConsumerExample.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperRevertingConsumerExample.sol new file mode 100644 index 0000000..5d9f68c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperRevertingConsumerExample.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; + +import "../VRFV2WrapperConsumerBase.sol"; +import "../ConfirmedOwner.sol"; + +contract VRFV2WrapperRevertingConsumerExample is VRFV2WrapperConsumerBase, ConfirmedOwner { + constructor(address _link, address _vrfV2Wrapper) + ConfirmedOwner(msg.sender) + VRFV2WrapperConsumerBase(_link, _vrfV2Wrapper) + {} + + function makeRequest( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords + ) external onlyOwner returns (uint256 requestId) { + return requestRandomness(_callbackGasLimit, _requestConfirmations, _numWords); + } + + function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal pure override { + revert("reverting example"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperUnderFundingConsumer.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperUnderFundingConsumer.sol new file mode 100644 index 0000000..05fff38 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/VRFV2WrapperUnderFundingConsumer.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../ConfirmedOwner.sol"; +import "../interfaces/LinkTokenInterface.sol"; +import "../interfaces/VRFV2WrapperInterface.sol"; + +contract VRFV2WrapperUnderFundingConsumer is ConfirmedOwner { + LinkTokenInterface internal immutable LINK; + VRFV2WrapperInterface internal immutable VRF_V2_WRAPPER; + + constructor(address _link, address _vrfV2Wrapper) ConfirmedOwner(msg.sender) { + LINK = LinkTokenInterface(_link); + VRF_V2_WRAPPER = VRFV2WrapperInterface(_vrfV2Wrapper); + } + + function makeRequest( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords + ) external onlyOwner { + LINK.transferAndCall( + address(VRF_V2_WRAPPER), + // Pay less than the needed amount + VRF_V2_WRAPPER.calculateRequestPrice(_callbackGasLimit) - 1, + abi.encode(_callbackGasLimit, _requestConfirmations, _numWords) + ); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/vendor/MockOVMCrossDomainMessenger.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/vendor/MockOVMCrossDomainMessenger.sol new file mode 100644 index 0000000..ebb53f4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/vendor/MockOVMCrossDomainMessenger.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.6 <0.9.0; + +import "../../dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol"; + +/** + * @title iOVM_CrossDomainMessenger + */ +interface iOVM_CrossDomainMessenger { + /********** + * Events * + **********/ + + event SentMessage(bytes message); + event RelayedMessage(bytes32 msgHash); + event FailedRelayedMessage(bytes32 msgHash); + + /************* + * Variables * + *************/ + + function xDomainMessageSender() external view returns (address); + + /******************** + * Public Functions * + ********************/ + + /** + * Sends a cross domain message to the target messenger. + * @param _target Target contract address. + * @param _message Message to send to the target. + * @param _gasLimit Gas limit for the provided message. + */ + function sendMessage( + address _target, + bytes calldata _message, + uint32 _gasLimit + ) external; +} + +contract MockOVMCrossDomainMessenger is iOVM_CrossDomainMessenger{ + address internal mockMessageSender; + + constructor(address sender) { + mockMessageSender = sender; + } + + function xDomainMessageSender() external view override returns (address) { + return mockMessageSender; + } + + function _setMockMessageSender(address sender) external { + mockMessageSender = sender; + } + + /******************** + * Public Functions * + ********************/ + + /** + * Sends a cross domain message to the target messenger. + * @param _target Target contract address. + * @param _message Message to send to the target. + * @param _gasLimit Gas limit for the provided message. + */ + function sendMessage( + address _target, + bytes calldata _message, + uint32 _gasLimit + ) external override { + Address.functionCall(_target, _message, "sendMessage reverted"); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/vendor/MultiSend.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/vendor/MultiSend.sol new file mode 100644 index 0000000..15a7d2f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/tests/vendor/MultiSend.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.7.0 <0.9.0; + +/// @title Multi Send - Allows to batch multiple transactions into one. +/// @author Nick Dodson - +/// @author Gonçalo Sá - +/// @author Stefan George - +/// @author Richard Meissner - +contract MultiSend { + address private immutable multisendSingleton; + + constructor() { + multisendSingleton = address(this); + } + + /// @dev Sends multiple transactions and reverts all if one fails. + /// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of + /// operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte), + /// to as a address (=> 20 bytes), + /// value as a uint256 (=> 32 bytes), + /// data length as a uint256 (=> 32 bytes), + /// data as bytes. + /// see abi.encodePacked for more information on packed encoding + /// @notice This method is payable as delegatecalls keep the msg.value from the previous call + /// If the calling method (e.g. execTransaction) received ETH this would revert otherwise + function multiSend(bytes memory transactions) public payable { + require(address(this) != multisendSingleton, "MultiSend should only be called via delegatecall"); + // solhint-disable-next-line no-inline-assembly + assembly { + let length := mload(transactions) + let i := 0x20 + for { + // Pre block is not used in "while mode" + } lt(i, length) { + // Post block is not used in "while mode" + } { + // First byte of the data is the operation. + // We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word). + // This will also zero out unused data. + let operation := shr(0xf8, mload(add(transactions, i))) + // We offset the load address by 1 byte (operation byte) + // We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data. + let to := shr(0x60, mload(add(transactions, add(i, 0x01)))) + // We offset the load address by 21 byte (operation byte + 20 address bytes) + let value := mload(add(transactions, add(i, 0x15))) + // We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes) + let dataLength := mload(add(transactions, add(i, 0x35))) + // We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes) + let data := add(transactions, add(i, 0x55)) + let success := 0 + switch operation + case 0 { + success := call(gas(), to, value, data, dataLength, 0, 0) + } + case 1 { + success := delegatecall(gas(), to, data, dataLength, 0, 0) + } + if eq(success, 0) { + revert(0, 0) + } + // Next entry starts at 85 byte + data length + i := add(i, add(0x55, dataLength)) + } + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/CronUpkeep.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/CronUpkeep.sol new file mode 100644 index 0000000..5628cba --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/CronUpkeep.sol @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: MIT + +/** + The Cron contract is a chainlink keepers-powered cron job runner for smart contracts. + The contract enables developers to trigger actions on various targets using cron + strings to specify the cadence. For example, a user may have 3 tasks that require + regular service in their dapp ecosystem: + 1) 0xAB..CD, update(1), "0 0 * * *" --> runs update(1) on 0xAB..CD daily at midnight + 2) 0xAB..CD, update(2), "30 12 * * 0-4" --> runs update(2) on 0xAB..CD weekdays at 12:30 + 3) 0x12..34, trigger(), "0 * * * *" --> runs trigger() on 0x12..34 hourly + + To use this contract, a user first deploys this contract and registers it on the chainlink + keeper registry. Then the user adds cron jobs by following these steps: + 1) Convert a cron string to an encoded cron spec by calling encodeCronString() + 2) Take the encoding, target, and handler, and create a job by sending a tx to createCronJob() + 3) Cron job is running :) +*/ + +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/security/Pausable.sol"; +import "@openzeppelin/contracts/proxy/Proxy.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "../ConfirmedOwner.sol"; +import "../KeeperBase.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; +import {Cron as CronInternal, Spec} from "../libraries/internal/Cron.sol"; +import {Cron as CronExternal} from "../libraries/external/Cron.sol"; +import {getRevertMsg} from "../utils/utils.sol"; + +/** + * @title The CronUpkeep contract + * @notice A keeper-compatible contract that runs various tasks on cron schedules. + * Users must use the encodeCronString() function to encode their cron jobs before + * setting them. This keeps all the string manipulation off chain and reduces gas costs. + */ +contract CronUpkeep is KeeperCompatibleInterface, KeeperBase, ConfirmedOwner, Pausable, Proxy { + using EnumerableSet for EnumerableSet.UintSet; + + event CronJobExecuted(uint256 indexed id, uint256 timestamp); + event CronJobCreated(uint256 indexed id, address target, bytes handler); + event CronJobUpdated(uint256 indexed id, address target, bytes handler); + event CronJobDeleted(uint256 indexed id); + + error CallFailed(uint256 id, string reason); + error CronJobIDNotFound(uint256 id); + error ExceedsMaxJobs(); + error InvalidHandler(); + error TickInFuture(); + error TickTooOld(); + error TickDoesntMatchSpec(); + + address immutable s_delegate; + uint256 public immutable s_maxJobs; + uint256 private s_nextCronJobID = 1; + EnumerableSet.UintSet private s_activeCronJobIDs; + + mapping(uint256 => uint256) private s_lastRuns; + mapping(uint256 => Spec) private s_specs; + mapping(uint256 => address) private s_targets; + mapping(uint256 => bytes) private s_handlers; + mapping(uint256 => bytes32) private s_handlerSignatures; + + /** + * @param owner the initial owner of the contract + * @param delegate the contract to delegate checkUpkeep calls to + * @param maxJobs the max number of cron jobs this contract will support + * @param firstJob an optional encoding of the first cron job + */ + constructor( + address owner, + address delegate, + uint256 maxJobs, + bytes memory firstJob + ) ConfirmedOwner(owner) { + s_delegate = delegate; + s_maxJobs = maxJobs; + if (firstJob.length > 0) { + (address target, bytes memory handler, Spec memory spec) = abi.decode(firstJob, (address, bytes, Spec)); + createCronJobFromSpec(target, handler, spec); + } + } + + /** + * @notice Executes the cron job with id encoded in performData + * @param performData abi encoding of cron job ID and the cron job's next run-at datetime + */ + function performUpkeep(bytes calldata performData) external override whenNotPaused { + (uint256 id, uint256 tickTime, address target, bytes memory handler) = abi.decode( + performData, + (uint256, uint256, address, bytes) + ); + validate(id, tickTime, target, handler); + s_lastRuns[id] = block.timestamp; + (bool success, bytes memory payload) = target.call(handler); + if (!success) { + revert CallFailed(id, getRevertMsg(payload)); + } + emit CronJobExecuted(id, block.timestamp); + } + + /** + * @notice Creates a cron job from the given encoded spec + * @param target the destination contract of a cron job + * @param handler the function signature on the target contract to call + * @param encodedCronSpec abi encoding of a cron spec + */ + function createCronJobFromEncodedSpec( + address target, + bytes memory handler, + bytes memory encodedCronSpec + ) external onlyOwner { + if (s_activeCronJobIDs.length() >= s_maxJobs) { + revert ExceedsMaxJobs(); + } + Spec memory spec = abi.decode(encodedCronSpec, (Spec)); + createCronJobFromSpec(target, handler, spec); + } + + /** + * @notice Updates a cron job from the given encoded spec + * @param id the id of the cron job to update + * @param newTarget the destination contract of a cron job + * @param newHandler the function signature on the target contract to call + * @param newEncodedCronSpec abi encoding of a cron spec + */ + function updateCronJob( + uint256 id, + address newTarget, + bytes memory newHandler, + bytes memory newEncodedCronSpec + ) external onlyOwner onlyValidCronID(id) { + Spec memory newSpec = abi.decode(newEncodedCronSpec, (Spec)); + s_targets[id] = newTarget; + s_handlers[id] = newHandler; + s_specs[id] = newSpec; + s_handlerSignatures[id] = handlerSig(newTarget, newHandler); + emit CronJobUpdated(id, newTarget, newHandler); + } + + /** + * @notice Deletes the cron job matching the provided id. Reverts if + * the id is not found. + * @param id the id of the cron job to delete + */ + function deleteCronJob(uint256 id) external onlyOwner onlyValidCronID(id) { + delete s_lastRuns[id]; + delete s_specs[id]; + delete s_targets[id]; + delete s_handlers[id]; + delete s_handlerSignatures[id]; + s_activeCronJobIDs.remove(id); + emit CronJobDeleted(id); + } + + /** + * @notice Pauses the contract, which prevents executing performUpkeep + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice Unpauses the contract + */ + function unpause() external onlyOwner { + _unpause(); + } + + /** + * @notice Get the id of an eligible cron job + * @return upkeepNeeded signals if upkeep is needed, performData is an abi encoding + * of the id and "next tick" of the elligible cron job + */ + function checkUpkeep(bytes calldata) external override whenNotPaused cannotExecute returns (bool, bytes memory) { + _delegate(s_delegate); + } + + /** + * @notice gets a list of active cron job IDs + * @return list of active cron job IDs + */ + function getActiveCronJobIDs() external view returns (uint256[] memory) { + uint256 length = s_activeCronJobIDs.length(); + uint256[] memory jobIDs = new uint256[](length); + for (uint256 idx = 0; idx < length; idx++) { + jobIDs[idx] = s_activeCronJobIDs.at(idx); + } + return jobIDs; + } + + /** + * @notice gets a cron job + * @param id the cron job ID + * @return target - the address a cron job forwards the eth tx to + handler - the encoded function sig to execute when forwarding a tx + cronString - the string representing the cron job + nextTick - the timestamp of the next time the cron job will run + */ + function getCronJob(uint256 id) + external + view + onlyValidCronID(id) + returns ( + address target, + bytes memory handler, + string memory cronString, + uint256 nextTick + ) + { + Spec memory spec = s_specs[id]; + return (s_targets[id], s_handlers[id], CronExternal.toCronString(spec), CronExternal.nextTick(spec)); + } + + /** + * @notice Adds a cron spec to storage and the ID to the list of jobs + * @param target the destination contract of a cron job + * @param handler the function signature on the target contract to call + * @param spec the cron spec to create + */ + function createCronJobFromSpec( + address target, + bytes memory handler, + Spec memory spec + ) internal { + uint256 newID = s_nextCronJobID; + s_activeCronJobIDs.add(newID); + s_targets[newID] = target; + s_handlers[newID] = handler; + s_specs[newID] = spec; + s_lastRuns[newID] = block.timestamp; + s_handlerSignatures[newID] = handlerSig(target, handler); + s_nextCronJobID++; + emit CronJobCreated(newID, target, handler); + } + + function _implementation() internal view override returns (address) { + return s_delegate; + } + + /** + * @notice validates the input to performUpkeep + * @param id the id of the cron job + * @param tickTime the observed tick time + * @param target the contract to forward the tx to + * @param handler the handler of the contract receiving the forwarded tx + */ + function validate( + uint256 id, + uint256 tickTime, + address target, + bytes memory handler + ) private { + tickTime = tickTime - (tickTime % 60); // remove seconds from tick time + if (block.timestamp < tickTime) { + revert TickInFuture(); + } + if (tickTime <= s_lastRuns[id]) { + revert TickTooOld(); + } + if (!CronInternal.matches(s_specs[id], tickTime)) { + revert TickDoesntMatchSpec(); + } + if (handlerSig(target, handler) != s_handlerSignatures[id]) { + revert InvalidHandler(); + } + } + + /** + * @notice returns a unique identifier for target/handler pairs + * @param target the contract to forward the tx to + * @param handler the handler of the contract receiving the forwarded tx + * @return a hash of the inputs + */ + function handlerSig(address target, bytes memory handler) private pure returns (bytes32) { + return keccak256(abi.encodePacked(target, handler)); + } + + modifier onlyValidCronID(uint256 id) { + if (!s_activeCronJobIDs.contains(id)) { + revert CronJobIDNotFound(id); + } + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/CronUpkeepDelegate.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/CronUpkeepDelegate.sol new file mode 100644 index 0000000..ec2c2a0 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/CronUpkeepDelegate.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {Cron, Spec} from "../libraries/internal/Cron.sol"; + +/** + * @title The CronUpkeepDelegate contract + * @notice This contract serves as a delegate for all instances of CronUpkeep. Those contracts + * delegate their checkUpkeep calls onto this contract. Utilizing this pattern reduces the size + * of the CronUpkeep contracts. + */ +contract CronUpkeepDelegate { + using EnumerableSet for EnumerableSet.UintSet; + using Cron for Spec; + + address private s_owner; // from ConfirmedOwner + address private s_delegate; + uint256 private s_nextCronJobID; + EnumerableSet.UintSet private s_activeCronJobIDs; + mapping(uint256 => uint256) private s_lastRuns; + mapping(uint256 => Spec) private s_specs; + mapping(uint256 => address) private s_targets; + mapping(uint256 => bytes) private s_handlers; + + /** + * @notice Get the id of an eligible cron job + * @return upkeepNeeded signals if upkeep is needed, performData is an abi encoding + * of the id and "next tick" of the eligible cron job + */ + function checkUpkeep(bytes calldata) external view returns (bool, bytes memory) { + // DEV: start at a random spot in the list so that checks are + // spread evenly among cron jobs + uint256 numCrons = s_activeCronJobIDs.length(); + if (numCrons == 0) { + return (false, bytes("")); + } + uint256 startIdx = block.number % numCrons; + bool result; + bytes memory payload; + (result, payload) = checkInRange(startIdx, numCrons); + if (result) { + return (result, payload); + } + (result, payload) = checkInRange(0, startIdx); + if (result) { + return (result, payload); + } + return (false, bytes("")); + } + + /** + * @notice checks the cron jobs in a given range + * @param start the starting id to check (inclusive) + * @param end the ending id to check (exclusive) + * @return upkeepNeeded signals if upkeep is needed, performData is an abi encoding + * of the id and "next tick" of the eligible cron job + */ + function checkInRange(uint256 start, uint256 end) private view returns (bool, bytes memory) { + uint256 id; + uint256 lastTick; + for (uint256 idx = start; idx < end; idx++) { + id = s_activeCronJobIDs.at(idx); + lastTick = s_specs[id].lastTick(); + if (lastTick > s_lastRuns[id]) { + return (true, abi.encode(id, lastTick, s_targets[id], s_handlers[id])); + } + } + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/EthBalanceMonitor.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/EthBalanceMonitor.sol new file mode 100644 index 0000000..90d6fe7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/upkeeps/EthBalanceMonitor.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import "../ConfirmedOwner.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; +import "@openzeppelin/contracts/security/Pausable.sol"; + +/** + * @title The EthBalanceMonitor contract + * @notice A keeper-compatible contract that monitors and funds eth addresses + */ +contract EthBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatibleInterface { + // observed limit of 45K + 10k buffer + uint256 private constant MIN_GAS_FOR_TRANSFER = 55_000; + + event FundsAdded(uint256 amountAdded, uint256 newBalance, address sender); + event FundsWithdrawn(uint256 amountWithdrawn, address payee); + event TopUpSucceeded(address indexed recipient); + event TopUpFailed(address indexed recipient); + event KeeperRegistryAddressUpdated(address oldAddress, address newAddress); + event MinWaitPeriodUpdated(uint256 oldMinWaitPeriod, uint256 newMinWaitPeriod); + + error InvalidWatchList(); + error OnlyKeeperRegistry(); + error DuplicateAddress(address duplicate); + + struct Target { + bool isActive; + uint96 minBalanceWei; + uint96 topUpAmountWei; + uint56 lastTopUpTimestamp; // enough space for 2 trillion years + } + + address private s_keeperRegistryAddress; + uint256 private s_minWaitPeriodSeconds; + address[] private s_watchList; + mapping(address => Target) internal s_targets; + + /** + * @param keeperRegistryAddress The address of the keeper registry contract + * @param minWaitPeriodSeconds The minimum wait period for addresses between funding + */ + constructor(address keeperRegistryAddress, uint256 minWaitPeriodSeconds) ConfirmedOwner(msg.sender) { + setKeeperRegistryAddress(keeperRegistryAddress); + setMinWaitPeriodSeconds(minWaitPeriodSeconds); + } + + /** + * @notice Sets the list of addresses to watch and their funding parameters + * @param addresses the list of addresses to watch + * @param minBalancesWei the minimum balances for each address + * @param topUpAmountsWei the amount to top up each address + */ + function setWatchList( + address[] calldata addresses, + uint96[] calldata minBalancesWei, + uint96[] calldata topUpAmountsWei + ) external onlyOwner { + if (addresses.length != minBalancesWei.length || addresses.length != topUpAmountsWei.length) { + revert InvalidWatchList(); + } + address[] memory oldWatchList = s_watchList; + for (uint256 idx = 0; idx < oldWatchList.length; idx++) { + s_targets[oldWatchList[idx]].isActive = false; + } + for (uint256 idx = 0; idx < addresses.length; idx++) { + if (s_targets[addresses[idx]].isActive) { + revert DuplicateAddress(addresses[idx]); + } + if (addresses[idx] == address(0)) { + revert InvalidWatchList(); + } + if (topUpAmountsWei[idx] == 0) { + revert InvalidWatchList(); + } + s_targets[addresses[idx]] = Target({ + isActive: true, + minBalanceWei: minBalancesWei[idx], + topUpAmountWei: topUpAmountsWei[idx], + lastTopUpTimestamp: 0 + }); + } + s_watchList = addresses; + } + + /** + * @notice Gets a list of addresses that are under funded + * @return list of addresses that are underfunded + */ + function getUnderfundedAddresses() public view returns (address[] memory) { + address[] memory watchList = s_watchList; + address[] memory needsFunding = new address[](watchList.length); + uint256 count = 0; + uint256 minWaitPeriod = s_minWaitPeriodSeconds; + uint256 balance = address(this).balance; + Target memory target; + for (uint256 idx = 0; idx < watchList.length; idx++) { + target = s_targets[watchList[idx]]; + if ( + target.lastTopUpTimestamp + minWaitPeriod <= block.timestamp && + balance >= target.topUpAmountWei && + watchList[idx].balance < target.minBalanceWei + ) { + needsFunding[count] = watchList[idx]; + count++; + balance -= target.topUpAmountWei; + } + } + if (count != watchList.length) { + assembly { + mstore(needsFunding, count) + } + } + return needsFunding; + } + + /** + * @notice Send funds to the addresses provided + * @param needsFunding the list of addresses to fund (addresses must be pre-approved) + */ + function topUp(address[] memory needsFunding) public whenNotPaused { + uint256 minWaitPeriodSeconds = s_minWaitPeriodSeconds; + Target memory target; + for (uint256 idx = 0; idx < needsFunding.length; idx++) { + target = s_targets[needsFunding[idx]]; + if ( + target.isActive && + target.lastTopUpTimestamp + minWaitPeriodSeconds <= block.timestamp && + needsFunding[idx].balance < target.minBalanceWei + ) { + bool success = payable(needsFunding[idx]).send(target.topUpAmountWei); + if (success) { + s_targets[needsFunding[idx]].lastTopUpTimestamp = uint56(block.timestamp); + emit TopUpSucceeded(needsFunding[idx]); + } else { + emit TopUpFailed(needsFunding[idx]); + } + } + if (gasleft() < MIN_GAS_FOR_TRANSFER) { + return; + } + } + } + + /** + * @notice Get list of addresses that are underfunded and return keeper-compatible payload + * @return upkeepNeeded signals if upkeep is needed, performData is an abi encoded list of addresses that need funds + */ + function checkUpkeep(bytes calldata) + external + view + override + whenNotPaused + returns (bool upkeepNeeded, bytes memory performData) + { + address[] memory needsFunding = getUnderfundedAddresses(); + upkeepNeeded = needsFunding.length > 0; + performData = abi.encode(needsFunding); + return (upkeepNeeded, performData); + } + + /** + * @notice Called by keeper to send funds to underfunded addresses + * @param performData The abi encoded list of addresses to fund + */ + function performUpkeep(bytes calldata performData) external override onlyKeeperRegistry whenNotPaused { + address[] memory needsFunding = abi.decode(performData, (address[])); + topUp(needsFunding); + } + + /** + * @notice Withdraws the contract balance + * @param amount The amount of eth (in wei) to withdraw + * @param payee The address to pay + */ + function withdraw(uint256 amount, address payable payee) external onlyOwner { + require(payee != address(0)); + emit FundsWithdrawn(amount, payee); + payee.transfer(amount); + } + + /** + * @notice Receive funds + */ + receive() external payable { + emit FundsAdded(msg.value, address(this).balance, msg.sender); + } + + /** + * @notice Sets the keeper registry address + */ + function setKeeperRegistryAddress(address keeperRegistryAddress) public onlyOwner { + require(keeperRegistryAddress != address(0)); + emit KeeperRegistryAddressUpdated(s_keeperRegistryAddress, keeperRegistryAddress); + s_keeperRegistryAddress = keeperRegistryAddress; + } + + /** + * @notice Sets the minimum wait period (in seconds) for addresses between funding + */ + function setMinWaitPeriodSeconds(uint256 period) public onlyOwner { + emit MinWaitPeriodUpdated(s_minWaitPeriodSeconds, period); + s_minWaitPeriodSeconds = period; + } + + /** + * @notice Gets the keeper registry address + */ + function getKeeperRegistryAddress() external view returns (address keeperRegistryAddress) { + return s_keeperRegistryAddress; + } + + /** + * @notice Gets the minimum wait period + */ + function getMinWaitPeriodSeconds() external view returns (uint256) { + return s_minWaitPeriodSeconds; + } + + /** + * @notice Gets the list of addresses being watched + */ + function getWatchList() external view returns (address[] memory) { + return s_watchList; + } + + /** + * @notice Gets configuration information for an address on the watchlist + */ + function getAccountInfo(address targetAddress) + external + view + returns ( + bool isActive, + uint96 minBalanceWei, + uint96 topUpAmountWei, + uint56 lastTopUpTimestamp + ) + { + Target memory target = s_targets[targetAddress]; + return (target.isActive, target.minBalanceWei, target.topUpAmountWei, target.lastTopUpTimestamp); + } + + /** + * @notice Pauses the contract, which prevents executing performUpkeep + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice Unpauses the contract + */ + function unpause() external onlyOwner { + _unpause(); + } + + modifier onlyKeeperRegistry() { + if (msg.sender != s_keeperRegistryAddress) { + revert OnlyKeeperRegistry(); + } + _; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/utils/utils.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/utils/utils.sol new file mode 100644 index 0000000..8ad933e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/utils/utils.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @notice getRevertMsg extracts a revert reason from a failed contract call + */ +function getRevertMsg(bytes memory payload) pure returns (string memory) { + if (payload.length < 68) return "transaction reverted silently"; + assembly { + payload := add(payload, 0x04) + } + return abi.decode(payload, (string)); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/BufferChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/BufferChainlink.sol new file mode 100644 index 0000000..761fac9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/BufferChainlink.sol @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @dev A library for working with mutable byte buffers in Solidity. + * + * Byte buffers are mutable and expandable, and provide a variety of primitives + * for writing to them. At any time you can fetch a bytes object containing the + * current contents of the buffer. The bytes object should not be stored between + * operations, as it may change due to resizing of the buffer. + */ +library BufferChainlink { + /** + * @dev Represents a mutable buffer. Buffers have a current value (buf) and + * a capacity. The capacity may be longer than the current value, in + * which case it can be extended without the need to allocate more memory. + */ + struct buffer { + bytes buf; + uint256 capacity; + } + + /** + * @dev Initializes a buffer with an initial capacity. + * @param buf The buffer to initialize. + * @param capacity The number of bytes of space to allocate the buffer. + * @return The buffer, for chaining. + */ + function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) { + if (capacity % 32 != 0) { + capacity += 32 - (capacity % 32); + } + // Allocate space for the buffer data + buf.capacity = capacity; + assembly { + let ptr := mload(0x40) + mstore(buf, ptr) + mstore(ptr, 0) + mstore(0x40, add(32, add(ptr, capacity))) + } + return buf; + } + + /** + * @dev Initializes a new buffer from an existing bytes object. + * Changes to the buffer may mutate the original value. + * @param b The bytes object to initialize the buffer with. + * @return A new buffer. + */ + function fromBytes(bytes memory b) internal pure returns (buffer memory) { + buffer memory buf; + buf.buf = b; + buf.capacity = b.length; + return buf; + } + + function resize(buffer memory buf, uint256 capacity) private pure { + bytes memory oldbuf = buf.buf; + init(buf, capacity); + append(buf, oldbuf); + } + + function max(uint256 a, uint256 b) private pure returns (uint256) { + if (a > b) { + return a; + } + return b; + } + + /** + * @dev Sets buffer length to 0. + * @param buf The buffer to truncate. + * @return The original buffer, for chaining.. + */ + function truncate(buffer memory buf) internal pure returns (buffer memory) { + assembly { + let bufptr := mload(buf) + mstore(bufptr, 0) + } + return buf; + } + + /** + * @dev Writes a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The start offset to write to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function write( + buffer memory buf, + uint256 off, + bytes memory data, + uint256 len + ) internal pure returns (buffer memory) { + require(len <= data.length); + + if (off + len > buf.capacity) { + resize(buf, max(buf.capacity, len + off) * 2); + } + + uint256 dest; + uint256 src; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Start address = buffer address + offset + sizeof(buffer length) + dest := add(add(bufptr, 32), off) + // Update buffer length if we're extending it + if gt(add(len, off), buflen) { + mstore(bufptr, add(len, off)) + } + src := add(data, 32) + } + + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + unchecked { + uint256 mask = (256**(32 - len)) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + return buf; + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @param len The number of bytes to copy. + * @return The original buffer, for chaining. + */ + function append( + buffer memory buf, + bytes memory data, + uint256 len + ) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, len); + } + + /** + * @dev Appends a byte string to a buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, data.length); + } + + /** + * @dev Writes a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write the byte at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeUint8( + buffer memory buf, + uint256 off, + uint8 data + ) internal pure returns (buffer memory) { + if (off >= buf.capacity) { + resize(buf, buf.capacity * 2); + } + + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Length of existing buffer data + let buflen := mload(bufptr) + // Address = buffer address + sizeof(buffer length) + off + let dest := add(add(bufptr, off), 32) + mstore8(dest, data) + // Update buffer length if we extended it + if eq(off, buflen) { + mstore(bufptr, add(buflen, 1)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) { + return writeUint8(buf, buf.buf.length, data); + } + + /** + * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (left-aligned). + * @return The original buffer, for chaining. + */ + function write( + buffer memory buf, + uint256 off, + bytes32 data, + uint256 len + ) private pure returns (buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + unchecked { + uint256 mask = (256**len) - 1; + // Right-align data + data = data >> (8 * (32 - len)); + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + sizeof(buffer length) + off + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + } + return buf; + } + + /** + * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the + * capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function writeBytes20( + buffer memory buf, + uint256 off, + bytes20 data + ) internal pure returns (buffer memory) { + return write(buf, off, bytes32(data), 20); + } + + /** + * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chhaining. + */ + function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, bytes32(data), 20); + } + + /** + * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer, for chaining. + */ + function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { + return write(buf, buf.buf.length, data, 32); + } + + /** + * @dev Writes an integer to the buffer. Resizes if doing so would exceed + * the capacity of the buffer. + * @param buf The buffer to append to. + * @param off The offset to write at. + * @param data The data to append. + * @param len The number of bytes to write (right-aligned). + * @return The original buffer, for chaining. + */ + function writeInt( + buffer memory buf, + uint256 off, + uint256 data, + uint256 len + ) private pure returns (buffer memory) { + if (len + off > buf.capacity) { + resize(buf, (len + off) * 2); + } + + uint256 mask = (256**len) - 1; + assembly { + // Memory address of the buffer data + let bufptr := mload(buf) + // Address = buffer address + off + sizeof(buffer length) + len + let dest := add(add(bufptr, off), len) + mstore(dest, or(and(mload(dest), not(mask)), data)) + // Update buffer length if we extended it + if gt(add(off, len), mload(bufptr)) { + mstore(bufptr, add(off, len)) + } + } + return buf; + } + + /** + * @dev Appends a byte to the end of the buffer. Resizes if doing so would + * exceed the capacity of the buffer. + * @param buf The buffer to append to. + * @param data The data to append. + * @return The original buffer. + */ + function appendInt( + buffer memory buf, + uint256 data, + uint256 len + ) internal pure returns (buffer memory) { + return writeInt(buf, buf.buf.length, data, len); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/CBORChainlink.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/CBORChainlink.sol new file mode 100644 index 0000000..5ee0cec --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/CBORChainlink.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.19; + +import {BufferChainlink} from "./BufferChainlink.sol"; + +library CBORChainlink { + using BufferChainlink for BufferChainlink.buffer; + + uint8 private constant MAJOR_TYPE_INT = 0; + uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 private constant MAJOR_TYPE_BYTES = 2; + uint8 private constant MAJOR_TYPE_STRING = 3; + uint8 private constant MAJOR_TYPE_ARRAY = 4; + uint8 private constant MAJOR_TYPE_MAP = 5; + uint8 private constant MAJOR_TYPE_TAG = 6; + uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; + + uint8 private constant TAG_TYPE_BIGNUM = 2; + uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; + + function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure { + if(value <= 23) { + buf.appendUint8(uint8((major << 5) | value)); + } else if (value <= 0xFF) { + buf.appendUint8(uint8((major << 5) | 24)); + buf.appendInt(value, 1); + } else if (value <= 0xFFFF) { + buf.appendUint8(uint8((major << 5) | 25)); + buf.appendInt(value, 2); + } else if (value <= 0xFFFFFFFF) { + buf.appendUint8(uint8((major << 5) | 26)); + buf.appendInt(value, 4); + } else { + buf.appendUint8(uint8((major << 5) | 27)); + buf.appendInt(value, 8); + } + } + + function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure { + buf.appendUint8(uint8((major << 5) | 31)); + } + + function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure { + if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, value); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); + } + } + + function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure { + if(value < -0x10000000000000000) { + encodeSignedBigNum(buf, value); + } else if(value > 0xFFFFFFFFFFFFFFFF) { + encodeBigNum(buf, uint(value)); + } else if(value >= 0) { + encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value))); + } else { + encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value))); + } + } + + function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); + buf.append(value); + } + + function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); + encodeBytes(buf, abi.encode(value)); + } + + function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure { + buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); + encodeBytes(buf, abi.encode(uint256(-1 - input))); + } + + function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure { + encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); + buf.append(bytes(value)); + } + + function startArray(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); + } + + function startMap(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); + } + + function endSequence(BufferChainlink.buffer memory buf) internal pure { + encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/DateTime.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/DateTime.sol new file mode 100644 index 0000000..d375ca4 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/DateTime.sol @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: MIT + +// sourced from https://github.com/pipermerriam/ethereum-datetime + +pragma solidity ^0.8.0; + +library DateTime { + /* + * Date and Time utilities for ethereum contracts + * + */ + struct _DateTime { + uint16 year; + uint8 month; + uint8 day; + uint8 hour; + uint8 minute; + uint8 second; + uint8 weekday; + } + + uint256 constant DAY_IN_SECONDS = 86400; + uint256 constant YEAR_IN_SECONDS = 31536000; + uint256 constant LEAP_YEAR_IN_SECONDS = 31622400; + + uint256 constant HOUR_IN_SECONDS = 3600; + uint256 constant MINUTE_IN_SECONDS = 60; + + uint16 constant ORIGIN_YEAR = 1970; + + function isLeapYear(uint16 year) internal pure returns (bool) { + if (year % 4 != 0) { + return false; + } + if (year % 100 != 0) { + return true; + } + if (year % 400 != 0) { + return false; + } + return true; + } + + function leapYearsBefore(uint256 year) internal pure returns (uint256) { + year -= 1; + return year / 4 - year / 100 + year / 400; + } + + function getDaysInMonth(uint8 month, uint16 year) + internal + pure + returns (uint8) + { + if ( + month == 1 || + month == 3 || + month == 5 || + month == 7 || + month == 8 || + month == 10 || + month == 12 + ) { + return 31; + } else if (month == 4 || month == 6 || month == 9 || month == 11) { + return 30; + } else if (isLeapYear(year)) { + return 29; + } else { + return 28; + } + } + + function parseTimestamp(uint256 timestamp) + internal + pure + returns (_DateTime memory dt) + { + uint256 secondsAccountedFor = 0; + uint256 buf; + uint8 i; + + // Year + dt.year = getYear(timestamp); + buf = leapYearsBefore(dt.year) - leapYearsBefore(ORIGIN_YEAR); + + secondsAccountedFor += LEAP_YEAR_IN_SECONDS * buf; + secondsAccountedFor += YEAR_IN_SECONDS * (dt.year - ORIGIN_YEAR - buf); + + // Month + uint256 secondsInMonth; + for (i = 1; i <= 12; i++) { + secondsInMonth = DAY_IN_SECONDS * getDaysInMonth(i, dt.year); + if (secondsInMonth + secondsAccountedFor > timestamp) { + dt.month = i; + break; + } + secondsAccountedFor += secondsInMonth; + } + + // Day + for (i = 1; i <= getDaysInMonth(dt.month, dt.year); i++) { + if (DAY_IN_SECONDS + secondsAccountedFor > timestamp) { + dt.day = i; + break; + } + secondsAccountedFor += DAY_IN_SECONDS; + } + + // Hour + dt.hour = getHour(timestamp); + + // Minute + dt.minute = getMinute(timestamp); + + // Second + dt.second = getSecond(timestamp); + + // Day of week. + dt.weekday = getWeekday(timestamp); + } + + function getYear(uint256 timestamp) internal pure returns (uint16) { + uint256 secondsAccountedFor = 0; + uint16 year; + uint256 numLeapYears; + + // Year + year = uint16(ORIGIN_YEAR + timestamp / YEAR_IN_SECONDS); + numLeapYears = leapYearsBefore(year) - leapYearsBefore(ORIGIN_YEAR); + + secondsAccountedFor += LEAP_YEAR_IN_SECONDS * numLeapYears; + secondsAccountedFor += + YEAR_IN_SECONDS * + (year - ORIGIN_YEAR - numLeapYears); + + while (secondsAccountedFor > timestamp) { + if (isLeapYear(uint16(year - 1))) { + secondsAccountedFor -= LEAP_YEAR_IN_SECONDS; + } else { + secondsAccountedFor -= YEAR_IN_SECONDS; + } + year -= 1; + } + return year; + } + + function getMonth(uint256 timestamp) internal pure returns (uint8) { + return parseTimestamp(timestamp).month; + } + + function getDay(uint256 timestamp) internal pure returns (uint8) { + return parseTimestamp(timestamp).day; + } + + function getHour(uint256 timestamp) internal pure returns (uint8) { + return uint8((timestamp / 60 / 60) % 24); + } + + function getMinute(uint256 timestamp) internal pure returns (uint8) { + return uint8((timestamp / 60) % 60); + } + + function getSecond(uint256 timestamp) internal pure returns (uint8) { + return uint8(timestamp % 60); + } + + function getWeekday(uint256 timestamp) internal pure returns (uint8) { + return uint8((timestamp / DAY_IN_SECONDS + 4) % 7); + } + + function toTimestamp( + uint16 year, + uint8 month, + uint8 day + ) internal pure returns (uint256 timestamp) { + return toTimestamp(year, month, day, 0, 0, 0); + } + + function toTimestamp( + uint16 year, + uint8 month, + uint8 day, + uint8 hour + ) internal pure returns (uint256 timestamp) { + return toTimestamp(year, month, day, hour, 0, 0); + } + + function toTimestamp( + uint16 year, + uint8 month, + uint8 day, + uint8 hour, + uint8 minute + ) internal pure returns (uint256 timestamp) { + return toTimestamp(year, month, day, hour, minute, 0); + } + + function toTimestamp( + uint16 year, + uint8 month, + uint8 day, + uint8 hour, + uint8 minute, + uint8 second + ) internal pure returns (uint256 timestamp) { + uint16 i; + + // Year + for (i = ORIGIN_YEAR; i < year; i++) { + if (isLeapYear(i)) { + timestamp += LEAP_YEAR_IN_SECONDS; + } else { + timestamp += YEAR_IN_SECONDS; + } + } + + // Month + uint8[12] memory monthDayCounts; + monthDayCounts[0] = 31; + if (isLeapYear(year)) { + monthDayCounts[1] = 29; + } else { + monthDayCounts[1] = 28; + } + monthDayCounts[2] = 31; + monthDayCounts[3] = 30; + monthDayCounts[4] = 31; + monthDayCounts[5] = 30; + monthDayCounts[6] = 31; + monthDayCounts[7] = 31; + monthDayCounts[8] = 30; + monthDayCounts[9] = 31; + monthDayCounts[10] = 30; + monthDayCounts[11] = 31; + + for (i = 1; i < month; i++) { + timestamp += DAY_IN_SECONDS * monthDayCounts[i - 1]; + } + + // Day + timestamp += DAY_IN_SECONDS * (day - 1); + + // Hour + timestamp += HOUR_IN_SECONDS * (hour); + + // Minute + timestamp += MINUTE_IN_SECONDS * (minute); + + // Second + timestamp += second; + + return timestamp; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/ENSResolver.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/ENSResolver.sol new file mode 100644 index 0000000..eb92ced --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/ENSResolver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +abstract contract ENSResolver { + function addr(bytes32 node) public view virtual returns (address); +} diff --git a/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/Strings.sol b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/Strings.sol new file mode 100644 index 0000000..4a17bd9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/src/v0.8/vendor/Strings.sol @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: Apache 2.0 + +/* + * @title String & slice utility library for Solidity contracts. + * @author Nick Johnson + * + * @dev Functionality in this library is largely implemented using an + * abstraction called a 'slice'. A slice represents a part of a string - + * anything from the entire string to a single character, or even no + * characters at all (a 0-length slice). Since a slice only has to specify + * an offset and a length, copying and manipulating slices is a lot less + * expensive than copying and manipulating the strings they reference. + * + * To further reduce gas costs, most functions on slice that need to return + * a slice modify the original one instead of allocating a new one; for + * instance, `s.split(".")` will return the text up to the first '.', + * modifying s to only contain the remainder of the string after the '.'. + * In situations where you do not want to modify the original slice, you + * can make a copy first with `.copy()`, for example: + * `s.copy().split(".")`. Try and avoid using this idiom in loops; since + * Solidity has no memory management, it will result in allocating many + * short-lived slices that are later discarded. + * + * Functions that return two slices come in two versions: a non-allocating + * version that takes the second slice as an argument, modifying it in + * place, and an allocating version that allocates and returns the second + * slice; see `nextRune` for example. + * + * Functions that have to copy string data will return strings rather than + * slices; these can be cast back to slices for further processing if + * required. + * + * For convenience, some functions are provided with non-modifying + * variants that create a new slice and return both; for instance, + * `s.splitNew('.')` leaves s unmodified, and returns two values + * corresponding to the left and right parts of the string. + */ + +pragma solidity ^0.8.0; + +library strings { + struct slice { + uint256 _len; + uint256 _ptr; + } + + function memcpy( + uint256 dest, + uint256 src, + uint256 len + ) private pure { + // Copy word-length chunks while possible + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint256 mask = type(uint256).max; + if (len > 0) { + mask = 256**(32 - len) - 1; + } + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /* + * @dev Returns a slice containing the entire string. + * @param self The string to make a slice from. + * @return A newly allocated slice containing the entire string. + */ + function toSlice(string memory self) internal pure returns (slice memory) { + uint256 ptr; + assembly { + ptr := add(self, 0x20) + } + return slice(bytes(self).length, ptr); + } + + /* + * @dev Returns the length of a null-terminated bytes32 string. + * @param self The value to find the length of. + * @return The length of the string, from 0 to 32. + */ + function len(bytes32 self) internal pure returns (uint256) { + uint256 ret; + if (self == 0) return 0; + if (uint256(self) & type(uint128).max == 0) { + ret += 16; + self = bytes32(uint256(self) / 0x100000000000000000000000000000000); + } + if (uint256(self) & type(uint64).max == 0) { + ret += 8; + self = bytes32(uint256(self) / 0x10000000000000000); + } + if (uint256(self) & type(uint32).max == 0) { + ret += 4; + self = bytes32(uint256(self) / 0x100000000); + } + if (uint256(self) & type(uint16).max == 0) { + ret += 2; + self = bytes32(uint256(self) / 0x10000); + } + if (uint256(self) & type(uint8).max == 0) { + ret += 1; + } + return 32 - ret; + } + + /* + * @dev Returns a slice containing the entire bytes32, interpreted as a + * null-terminated utf-8 string. + * @param self The bytes32 value to convert to a slice. + * @return A new slice containing the value of the input argument up to the + * first null. + */ + function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { + // Allocate space for `self` in memory, copy it there, and point ret at it + assembly { + let ptr := mload(0x40) + mstore(0x40, add(ptr, 0x20)) + mstore(ptr, self) + mstore(add(ret, 0x20), ptr) + } + ret._len = len(self); + } + + /* + * @dev Returns a new slice containing the same data as the current slice. + * @param self The slice to copy. + * @return A new slice containing the same data as `self`. + */ + function copy(slice memory self) internal pure returns (slice memory) { + return slice(self._len, self._ptr); + } + + /* + * @dev Copies a slice to a new string. + * @param self The slice to copy. + * @return A newly allocated string containing the slice's text. + */ + function toString(slice memory self) internal pure returns (string memory) { + string memory ret = new string(self._len); + uint256 retptr; + assembly { + retptr := add(ret, 32) + } + + memcpy(retptr, self._ptr, self._len); + return ret; + } + + /* + * @dev Returns the length in runes of the slice. Note that this operation + * takes time proportional to the length of the slice; avoid using it + * in loops, and call `slice.empty()` if you only need to know whether + * the slice is empty or not. + * @param self The slice to operate on. + * @return The length of the slice in runes. + */ + function len(slice memory self) internal pure returns (uint256 l) { + // Starting at ptr-31 means the LSB will be the byte we care about + uint256 ptr = self._ptr - 31; + uint256 end = ptr + self._len; + for (l = 0; ptr < end; l++) { + uint8 b; + assembly { + b := and(mload(ptr), 0xFF) + } + if (b < 0x80) { + ptr += 1; + } else if (b < 0xE0) { + ptr += 2; + } else if (b < 0xF0) { + ptr += 3; + } else if (b < 0xF8) { + ptr += 4; + } else if (b < 0xFC) { + ptr += 5; + } else { + ptr += 6; + } + } + } + + /* + * @dev Returns true if the slice is empty (has a length of 0). + * @param self The slice to operate on. + * @return True if the slice is empty, False otherwise. + */ + function empty(slice memory self) internal pure returns (bool) { + return self._len == 0; + } + + /* + * @dev Returns a positive number if `other` comes lexicographically after + * `self`, a negative number if it comes before, or zero if the + * contents of the two slices are equal. Comparison is done per-rune, + * on unicode codepoints. + * @param self The first slice to compare. + * @param other The second slice to compare. + * @return The result of the comparison. + */ + function compare(slice memory self, slice memory other) + internal + pure + returns (int256) + { + uint256 shortest = self._len; + if (other._len < self._len) shortest = other._len; + + uint256 selfptr = self._ptr; + uint256 otherptr = other._ptr; + for (uint256 idx = 0; idx < shortest; idx += 32) { + uint256 a; + uint256 b; + assembly { + a := mload(selfptr) + b := mload(otherptr) + } + if (a != b) { + // Mask out irrelevant bytes and check again + uint256 mask = type(uint256).max; // 0xffff... + if (shortest < 32) { + mask = ~(2**(8 * (32 - shortest + idx)) - 1); + } + unchecked { + uint256 diff = (a & mask) - (b & mask); + if (diff != 0) return int256(diff); + } + } + selfptr += 32; + otherptr += 32; + } + return int256(self._len) - int256(other._len); + } + + /* + * @dev Returns true if the two slices contain the same text. + * @param self The first slice to compare. + * @param self The second slice to compare. + * @return True if the slices are equal, false otherwise. + */ + function equals(slice memory self, slice memory other) + internal + pure + returns (bool) + { + return compare(self, other) == 0; + } + + /* + * @dev Extracts the first rune in the slice into `rune`, advancing the + * slice to point to the next rune and returning `self`. + * @param self The slice to operate on. + * @param rune The slice that will contain the first rune. + * @return `rune`. + */ + function nextRune(slice memory self, slice memory rune) + internal + pure + returns (slice memory) + { + rune._ptr = self._ptr; + + if (self._len == 0) { + rune._len = 0; + return rune; + } + + uint256 l; + uint256 b; + // Load the first byte of the rune into the LSBs of b + assembly { + b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) + } + if (b < 0x80) { + l = 1; + } else if (b < 0xE0) { + l = 2; + } else if (b < 0xF0) { + l = 3; + } else { + l = 4; + } + + // Check for truncated codepoints + if (l > self._len) { + rune._len = self._len; + self._ptr += self._len; + self._len = 0; + return rune; + } + + self._ptr += l; + self._len -= l; + rune._len = l; + return rune; + } + + /* + * @dev Returns the first rune in the slice, advancing the slice to point + * to the next rune. + * @param self The slice to operate on. + * @return A slice containing only the first rune from `self`. + */ + function nextRune(slice memory self) + internal + pure + returns (slice memory ret) + { + nextRune(self, ret); + } + + /* + * @dev Returns the number of the first codepoint in the slice. + * @param self The slice to operate on. + * @return The number of the first codepoint in the slice. + */ + function ord(slice memory self) internal pure returns (uint256 ret) { + if (self._len == 0) { + return 0; + } + + uint256 word; + uint256 length; + uint256 divisor = 2**248; + + // Load the rune into the MSBs of b + assembly { + word := mload(mload(add(self, 32))) + } + uint256 b = word / divisor; + if (b < 0x80) { + ret = b; + length = 1; + } else if (b < 0xE0) { + ret = b & 0x1F; + length = 2; + } else if (b < 0xF0) { + ret = b & 0x0F; + length = 3; + } else { + ret = b & 0x07; + length = 4; + } + + // Check for truncated codepoints + if (length > self._len) { + return 0; + } + + for (uint256 i = 1; i < length; i++) { + divisor = divisor / 256; + b = (word / divisor) & 0xFF; + if (b & 0xC0 != 0x80) { + // Invalid UTF-8 sequence + return 0; + } + ret = (ret * 64) | (b & 0x3F); + } + + return ret; + } + + /* + * @dev Returns the keccak-256 hash of the slice. + * @param self The slice to hash. + * @return The hash of the slice. + */ + function keccak(slice memory self) internal pure returns (bytes32 ret) { + assembly { + ret := keccak256(mload(add(self, 32)), mload(self)) + } + } + + /* + * @dev Returns true if `self` starts with `needle`. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return True if the slice starts with the provided text, false otherwise. + */ + function startsWith(slice memory self, slice memory needle) + internal + pure + returns (bool) + { + if (self._len < needle._len) { + return false; + } + + if (self._ptr == needle._ptr) { + return true; + } + + bool equal; + assembly { + let length := mload(needle) + let selfptr := mload(add(self, 0x20)) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + return equal; + } + + /* + * @dev If `self` starts with `needle`, `needle` is removed from the + * beginning of `self`. Otherwise, `self` is unmodified. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return `self` + */ + function beyond(slice memory self, slice memory needle) + internal + pure + returns (slice memory) + { + if (self._len < needle._len) { + return self; + } + + bool equal = true; + if (self._ptr != needle._ptr) { + assembly { + let length := mload(needle) + let selfptr := mload(add(self, 0x20)) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + } + + if (equal) { + self._len -= needle._len; + self._ptr += needle._len; + } + + return self; + } + + /* + * @dev Returns true if the slice ends with `needle`. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return True if the slice starts with the provided text, false otherwise. + */ + function endsWith(slice memory self, slice memory needle) + internal + pure + returns (bool) + { + if (self._len < needle._len) { + return false; + } + + uint256 selfptr = self._ptr + self._len - needle._len; + + if (selfptr == needle._ptr) { + return true; + } + + bool equal; + assembly { + let length := mload(needle) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + + return equal; + } + + /* + * @dev If `self` ends with `needle`, `needle` is removed from the + * end of `self`. Otherwise, `self` is unmodified. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return `self` + */ + function until(slice memory self, slice memory needle) + internal + pure + returns (slice memory) + { + if (self._len < needle._len) { + return self; + } + + uint256 selfptr = self._ptr + self._len - needle._len; + bool equal = true; + if (selfptr != needle._ptr) { + assembly { + let length := mload(needle) + let needleptr := mload(add(needle, 0x20)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) + } + } + + if (equal) { + self._len -= needle._len; + } + + return self; + } + + // Returns the memory address of the first byte of the first occurrence of + // `needle` in `self`, or the first byte after `self` if not found. + function findPtr( + uint256 selflen, + uint256 selfptr, + uint256 needlelen, + uint256 needleptr + ) private pure returns (uint256) { + uint256 ptr = selfptr; + uint256 idx; + + if (needlelen <= selflen) { + if (needlelen <= 32) { + bytes32 mask; + if (needlelen > 0) { + mask = bytes32(~(2**(8 * (32 - needlelen)) - 1)); + } + + bytes32 needledata; + assembly { + needledata := and(mload(needleptr), mask) + } + + uint256 end = selfptr + selflen - needlelen; + bytes32 ptrdata; + assembly { + ptrdata := and(mload(ptr), mask) + } + + while (ptrdata != needledata) { + if (ptr >= end) return selfptr + selflen; + ptr++; + assembly { + ptrdata := and(mload(ptr), mask) + } + } + return ptr; + } else { + // For long needles, use hashing + bytes32 hash; + assembly { + hash := keccak256(needleptr, needlelen) + } + + for (idx = 0; idx <= selflen - needlelen; idx++) { + bytes32 testHash; + assembly { + testHash := keccak256(ptr, needlelen) + } + if (hash == testHash) return ptr; + ptr += 1; + } + } + } + return selfptr + selflen; + } + + // Returns the memory address of the first byte after the last occurrence of + // `needle` in `self`, or the address of `self` if not found. + function rfindPtr( + uint256 selflen, + uint256 selfptr, + uint256 needlelen, + uint256 needleptr + ) private pure returns (uint256) { + uint256 ptr; + + if (needlelen <= selflen) { + if (needlelen <= 32) { + bytes32 mask; + if (needlelen > 0) { + mask = bytes32(~(2**(8 * (32 - needlelen)) - 1)); + } + + bytes32 needledata; + assembly { + needledata := and(mload(needleptr), mask) + } + + ptr = selfptr + selflen - needlelen; + bytes32 ptrdata; + assembly { + ptrdata := and(mload(ptr), mask) + } + + while (ptrdata != needledata) { + if (ptr <= selfptr) return selfptr; + ptr--; + assembly { + ptrdata := and(mload(ptr), mask) + } + } + return ptr + needlelen; + } else { + // For long needles, use hashing + bytes32 hash; + assembly { + hash := keccak256(needleptr, needlelen) + } + ptr = selfptr + (selflen - needlelen); + while (ptr >= selfptr) { + bytes32 testHash; + assembly { + testHash := keccak256(ptr, needlelen) + } + if (hash == testHash) return ptr + needlelen; + ptr -= 1; + } + } + } + return selfptr; + } + + /* + * @dev Modifies `self` to contain everything from the first occurrence of + * `needle` to the end of the slice. `self` is set to the empty slice + * if `needle` is not found. + * @param self The slice to search and modify. + * @param needle The text to search for. + * @return `self`. + */ + function find(slice memory self, slice memory needle) + internal + pure + returns (slice memory) + { + uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); + self._len -= ptr - self._ptr; + self._ptr = ptr; + return self; + } + + /* + * @dev Modifies `self` to contain the part of the string from the start of + * `self` to the end of the first occurrence of `needle`. If `needle` + * is not found, `self` is set to the empty slice. + * @param self The slice to search and modify. + * @param needle The text to search for. + * @return `self`. + */ + function rfind(slice memory self, slice memory needle) + internal + pure + returns (slice memory) + { + uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); + self._len = ptr - self._ptr; + return self; + } + + /* + * @dev Splits the slice, setting `self` to everything after the first + * occurrence of `needle`, and `token` to everything before it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and `token` is set to the entirety of `self`. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @param token An output parameter to which the first token is written. + * @return `token`. + */ + function split( + slice memory self, + slice memory needle, + slice memory token + ) internal pure returns (slice memory) { + uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); + token._ptr = self._ptr; + token._len = ptr - self._ptr; + if (ptr == self._ptr + self._len) { + // Not found + self._len = 0; + } else { + self._len -= token._len + needle._len; + self._ptr = ptr + needle._len; + } + return token; + } + + /* + * @dev Splits the slice, setting `self` to everything after the first + * occurrence of `needle`, and returning everything before it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and the entirety of `self` is returned. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @return The part of `self` up to the first occurrence of `delim`. + */ + function split(slice memory self, slice memory needle) + internal + pure + returns (slice memory token) + { + split(self, needle, token); + } + + /* + * @dev Splits the slice, setting `self` to everything before the last + * occurrence of `needle`, and `token` to everything after it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and `token` is set to the entirety of `self`. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @param token An output parameter to which the first token is written. + * @return `token`. + */ + function rsplit( + slice memory self, + slice memory needle, + slice memory token + ) internal pure returns (slice memory) { + uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); + token._ptr = ptr; + token._len = self._len - (ptr - self._ptr); + if (ptr == self._ptr) { + // Not found + self._len = 0; + } else { + self._len -= token._len + needle._len; + } + return token; + } + + /* + * @dev Splits the slice, setting `self` to everything before the last + * occurrence of `needle`, and returning everything after it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and the entirety of `self` is returned. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @return The part of `self` after the last occurrence of `delim`. + */ + function rsplit(slice memory self, slice memory needle) + internal + pure + returns (slice memory token) + { + rsplit(self, needle, token); + } + + /* + * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. + * @param self The slice to search. + * @param needle The text to search for in `self`. + * @return The number of occurrences of `needle` found in `self`. + */ + function count(slice memory self, slice memory needle) + internal + pure + returns (uint256 cnt) + { + uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + + needle._len; + while (ptr <= self._ptr + self._len) { + cnt++; + ptr = + findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + + needle._len; + } + } + + /* + * @dev Returns True if `self` contains `needle`. + * @param self The slice to search. + * @param needle The text to search for in `self`. + * @return True if `needle` is found in `self`, false otherwise. + */ + function contains(slice memory self, slice memory needle) + internal + pure + returns (bool) + { + return + rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; + } + + /* + * @dev Returns a newly allocated string containing the concatenation of + * `self` and `other`. + * @param self The first slice to concatenate. + * @param other The second slice to concatenate. + * @return The concatenation of the two strings. + */ + function concat(slice memory self, slice memory other) + internal + pure + returns (string memory) + { + string memory ret = new string(self._len + other._len); + uint256 retptr; + assembly { + retptr := add(ret, 32) + } + memcpy(retptr, self._ptr, self._len); + memcpy(retptr + self._len, other._ptr, other._len); + return ret; + } + + /* + * @dev Joins an array of slices, using `self` as a delimiter, returning a + * newly allocated string. + * @param self The delimiter to use. + * @param parts A list of slices to join. + * @return A newly allocated string containing all the slices in `parts`, + * joined with `self`. + */ + function join(slice memory self, slice[] memory parts) + internal + pure + returns (string memory) + { + if (parts.length == 0) return ""; + + uint256 length = self._len * (parts.length - 1); + for (uint256 i = 0; i < parts.length; i++) length += parts[i]._len; + + string memory ret = new string(length); + uint256 retptr; + assembly { + retptr := add(ret, 32) + } + + for (uint256 i = 0; i < parts.length; i++) { + memcpy(retptr, parts[i]._ptr, parts[i]._len); + retptr += parts[i]._len; + if (i < parts.length - 1) { + memcpy(retptr, self._ptr, self._len); + retptr += self._len; + } + } + + return ret; + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/test-helpers/debug.ts b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/debug.ts new file mode 100644 index 0000000..8dbd5b6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/debug.ts @@ -0,0 +1,17 @@ +/** + * @packageDocumentation + * + * This file contains functionality for debugging tests, like creating loggers. + */ +import debug from "debug"; + +/** + * This creates a debug logger instance to be used within our internal code. + * + * @see https://www.npmjs.com/package/debug to see how to use the logger at runtime + * @see wallet.ts makes extensive use of this function. + * @param name The root namespace to assign to the log messages + */ +export function makeDebug(name: string): debug.Debugger { + return debug(name); +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/test-helpers/helpers.ts b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/helpers.ts new file mode 100644 index 0000000..f72232f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/helpers.ts @@ -0,0 +1,216 @@ +import { Contract, ContractTransaction } from "ethers"; +import type { providers } from "ethers"; +import { assert } from "chai"; +import { ethers } from "hardhat"; +import cbor from "cbor"; + +/** + * Convert string to hex bytes + * @param data string to onvert to hex bytes + */ +export function stringToBytes(data: string): string { + return ethers.utils.hexlify(ethers.utils.toUtf8Bytes(data)); +} + +/** + * Add a hex prefix to a hex string + * + * @param hex The hex string to prepend the hex prefix to + */ +export function addHexPrefix(hex: string): string { + return hex.startsWith("0x") ? hex : `0x${hex}`; +} + +/** + * Convert a number value to bytes32 format + * + * @param num The number value to convert to bytes32 format + */ +export function numToBytes32(num: Parameters[0]): string { + const hexNum = ethers.utils.hexlify(num); + const strippedNum = stripHexPrefix(hexNum); + if (strippedNum.length > 32 * 2) { + throw Error("Cannot convert number to bytes32 format, value is greater than maximum bytes32 value"); + } + return addHexPrefix(strippedNum.padStart(32 * 2, "0")); +} + +/** + * Retrieve single log from transaction + * + * @param tx The transaction to wait for, then extract logs from + * @param index The index of the log to retrieve + */ +export async function getLog(tx: ContractTransaction, index: number): Promise { + const logs = await getLogs(tx); + if (!logs[index]) { + throw Error("unable to extract log from transaction receipt"); + } + return logs[index]; +} + +/** + * Extract array of logs from a transaction + * + * @param tx The transaction to wait for, then extract logs from + */ +export async function getLogs(tx: ContractTransaction): Promise { + const receipt = await tx.wait(); + if (!receipt.logs) { + throw Error("unable to extract logs from transaction receipt"); + } + return receipt.logs; +} + +/** + * Convert a UTF-8 string into a bytes32 hex string representation + * + * The inverse function of [[parseBytes32String]] + * + * @param args The UTF-8 string representation to convert to a bytes32 hex string representation + */ +export function toBytes32String( + ...args: Parameters +): ReturnType { + return ethers.utils.formatBytes32String(...args); +} + +/** + * Strip the leading 0x hex prefix from a hex string + * + * @param hex The hex string to strip the leading hex prefix out of + */ +export function stripHexPrefix(hex: string): string { + if (!ethers.utils.isHexString(hex)) { + throw Error(`Expected valid hex string, got: "${hex}"`); + } + + return hex.replace("0x", ""); +} + +/** + * Create a buffer from a hex string + * + * @param hexstr The hex string to convert to a buffer + */ +export function hexToBuf(hexstr: string): Buffer { + return Buffer.from(stripHexPrefix(hexstr), "hex"); +} + +/** + * Decodes a CBOR hex string, and adds opening and closing brackets to the CBOR if they are not present. + * + * @param hexstr The hex string to decode + */ +export function decodeDietCBOR(hexstr: string) { + const buf = hexToBuf(hexstr); + + return cbor.decodeFirstSync(addCBORMapDelimiters(buf)); +} + +/** + * Add a starting and closing map characters to a CBOR encoding if they are not already present. + */ +export function addCBORMapDelimiters(buffer: Buffer): Buffer { + if (buffer[0] >> 5 === 5) { + return buffer; + } + + /** + * This is the opening character of a CBOR map. + * @see https://en.wikipedia.org/wiki/CBOR#CBOR_data_item_header + */ + const startIndefiniteLengthMap = Buffer.from([0xbf]); + /** + * This is the closing character in a CBOR map. + * @see https://en.wikipedia.org/wiki/CBOR#CBOR_data_item_header + */ + const endIndefiniteLengthMap = Buffer.from([0xff]); + return Buffer.concat([startIndefiniteLengthMap, buffer, endIndefiniteLengthMap], buffer.length + 2); +} + +/** + * Convert an Ether value to a wei amount + * + * @param args Ether value to convert to an Ether amount + */ +export function toWei(...args: Parameters): ReturnType { + return ethers.utils.parseEther(...args); +} + +/** + * Converts any number, BigNumber, hex string or Arrayish to a hex string. + * + * @param args Value to convert to a hex string + */ +export function toHex(...args: Parameters): ReturnType { + return ethers.utils.hexlify(...args); +} + +/** + * Increase the current time within the evm to 5 minutes past the current time + * + * @param provider The ethers provider to send the time increase request to + */ +export async function increaseTime5Minutes(provider: providers.JsonRpcProvider): Promise { + await increaseTimeBy(5 * 600, provider); +} + +/** + * Increase the current time within the evm to "n" seconds past the current time + * + * @param seconds The number of seconds to increase to the current time by + * @param provider The ethers provider to send the time increase request to + */ +export async function increaseTimeBy(seconds: number, provider: providers.JsonRpcProvider) { + await provider.send("evm_increaseTime", [seconds]); +} + +/** + * Instruct the provider to mine an additional block + * + * @param provider The ethers provider to instruct to mine an additional block + */ +export async function mineBlock(provider: providers.JsonRpcProvider) { + await provider.send("evm_mine", []); +} + +/** + * Parse out an evm word (32 bytes) into an address (20 bytes) representation + * + * @param hex The evm word in hex string format to parse the address + * out of. + */ +export function evmWordToAddress(hex?: string): string { + if (!hex) { + throw Error("Input not defined"); + } + + assert.equal(hex.slice(0, 26), "0x000000000000000000000000"); + return ethers.utils.getAddress(hex.slice(26)); +} + +/** + * Check that a contract's abi exposes the expected interface. + * + * @param contract The contract with the actual abi to check the expected exposed methods and getters against. + * @param expectedPublic The expected public exposed methods and getters to match against the actual abi. + */ +export function publicAbi(contract: Contract, expectedPublic: string[]) { + const actualPublic = []; + for (const m in contract.functions) { + if (!m.includes("(")) { + actualPublic.push(m); + } + } + + for (const method of actualPublic) { + const index = expectedPublic.indexOf(method); + assert.isAtLeast(index, 0, `#${method} is NOT expected to be public`); + } + + for (const method of expectedPublic) { + const index = actualPublic.indexOf(method); + assert.isAtLeast(index, 0, `#${method} is expected to be public`); + } +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/test-helpers/matchers.ts b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/matchers.ts new file mode 100644 index 0000000..46ca925 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/matchers.ts @@ -0,0 +1,46 @@ +import { BigNumber, BigNumberish } from "@ethersproject/bignumber"; +import { ContractReceipt } from "@ethersproject/contracts"; +import { assert, expect } from "chai"; + +/** + * Check that two big numbers are the same value. + * + * @param expected The expected value to match against + * @param actual The actual value to match against the expected value + * @param failureMessage Failure message to display if the actual value does not match the expected value. + */ +export function bigNumEquals(expected: BigNumberish, actual: BigNumberish, failureMessage?: string): void { + const msg = failureMessage ? ": " + failureMessage : ""; + assert( + BigNumber.from(expected).eq(BigNumber.from(actual)), + `BigNum (expected)${expected} is not (actual)${actual} ${msg}`, + ); +} + +/** + * Check that an evm operation reverts + * + * @param action The asynchronous action to execute, which should cause an evm revert. + * @param msg The failure message to display if the action __does not__ throw + */ +export async function evmRevert(action: (() => Promise) | Promise, msg?: string) { + if (msg) { + await expect(action).to.be.revertedWith(msg); + } else { + await expect(action).to.be.reverted; + } +} + +/** + * Assert that an event doesnt exist + * + * @param max The maximum allowable gas difference + * @param receipt1 The contract receipt to compare to + * @param receipt2 The contract receipt with a gas difference + */ +export function gasDiffLessThan(max: number, receipt1: ContractReceipt, receipt2: ContractReceipt) { + assert(receipt1, "receipt1 is not present for gas comparison"); + assert(receipt2, "receipt2 is not present for gas comparison"); + const diff = receipt2.gasUsed?.sub(receipt1.gasUsed || 0); + assert.isAbove(max, diff?.toNumber() || Infinity); +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/test-helpers/oracle.ts b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/oracle.ts new file mode 100644 index 0000000..9cec586 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/oracle.ts @@ -0,0 +1,375 @@ +/** + * @packageDocumentation + * + * This file provides convenience functions to interact with existing solidity contract abstraction libraries, such as + * @truffle/contract and ethers.js specifically for our `Oracle.sol` solidity smart contract. + */ +import { ethers } from "ethers"; +import { BigNumberish } from "ethers/utils"; +import { makeDebug } from "./debug"; +import { addCBORMapDelimiters, stripHexPrefix, toHex } from "./helpers"; +const debug = makeDebug("oracle"); + +/** + * Transaction options such as gasLimit, gasPrice, data, ... + */ +type TxOptions = Omit; + +/** + * A run request is an event emitted by `Oracle.sol` which triggers a job run + * on a receiving chainlink node watching for RunRequests coming from that + * specId + optionally requester. + */ +export interface RunRequest { + /** + * The ID of the job spec this request is targeting + * + * @solformat bytes32 + */ + specId: string; + /** + * The requester of the run + * + * @solformat address + */ + requester: string; + /** + * The ID of the request, check Oracle.sol#oracleRequest to see how its computed + * + * @solformat bytes32 + */ + requestId: string; + /** + * The amount of LINK used for payment + * + * @solformat uint256 + */ + payment: string; + /** + * The address of the contract instance to callback with the fulfillment result + * + * @solformat address + */ + callbackAddr: string; + /** + * The function selector of the method that the oracle should call after fulfillment + * + * @solformat bytes4 + */ + callbackFunc: string; + /** + * The expiration that the node should respond by before the requester can cancel + * + * @solformat uint256 + */ + expiration: string; + /** + * The specified data version + * + * @solformat uint256 + */ + dataVersion: number; + /** + * The CBOR encoded payload of the request + * + * @solformat bytes + */ + data: Buffer; + + /** + * The hash of the signature of the OracleRequest event. + * ```solidity + * event OracleRequest( + * bytes32 indexed specId, + * address requester, + * bytes32 requestId, + * uint256 payment, + * address callbackAddr, + * bytes4 callbackFunctionId, + * uint256 cancelExpiration, + * uint256 dataVersion, + * bytes data + * ); + * ``` + * Note: this is a property used for testing purposes only. + * It is not part of the actual run request. + * + * @solformat bytes32 + */ + topic: string; +} + +/** + * Convert the javascript format of the parameters needed to call the + * ```solidity + * function fulfillOracleRequest( + * bytes32 _requestId, + * uint256 _payment, + * address _callbackAddress, + * bytes4 _callbackFunctionId, + * uint256 _expiration, + * bytes32 _data + * ) + * ``` + * method on an Oracle.sol contract. + * + * @param runRequest The run request to flatten into the correct order to perform the `fulfillOracleRequest` function + * @param response The response to fulfill the run request with, if it is an ascii string, it is converted to bytes32 string + * @param txOpts Additional ethereum tx options + */ +export function convertFufillParams( + runRequest: RunRequest, + response: string, + txOpts: TxOptions = {}, +): [string, string, string, string, string, string, TxOptions] { + const d = debug.extend("fulfillOracleRequestParams"); + d("Response param: %s", response); + + const bytes32Len = 32 * 2 + 2; + const convertedResponse = response.length < bytes32Len ? ethers.utils.formatBytes32String(response) : response; + d("Converted Response param: %s", convertedResponse); + + return [ + runRequest.requestId, + runRequest.payment, + runRequest.callbackAddr, + runRequest.callbackFunc, + runRequest.expiration, + convertedResponse, + txOpts, + ]; +} + +/** + * Convert the javascript format of the parameters needed to call the + * ```solidity + * function fulfillOracleRequest2( + * bytes32 _requestId, + * uint256 _payment, + * address _callbackAddress, + * bytes4 _callbackFunctionId, + * uint256 _expiration, + * bytes memory _data + * ) + * ``` + * method on an Oracle.sol contract. + * + * @param runRequest The run request to flatten into the correct order to perform the `fulfillOracleRequest` function + * @param response The response to fulfill the run request with, if it is an ascii string, it is converted to bytes32 string + * @param txOpts Additional ethereum tx options + */ +export function convertFulfill2Params( + runRequest: RunRequest, + responseTypes: string[], + responseValues: string[], + txOpts: TxOptions = {}, +): [string, string, string, string, string, string, TxOptions] { + const d = debug.extend("fulfillOracleRequestParams"); + d("Response param: %s", responseValues); + const types = [...responseTypes]; + const values = [...responseValues]; + types.unshift("bytes32"); + values.unshift(runRequest.requestId); + const convertedResponse = ethers.utils.defaultAbiCoder.encode(types, values); + d("Encoded Response param: %s", convertedResponse); + return [ + runRequest.requestId, + runRequest.payment, + runRequest.callbackAddr, + runRequest.callbackFunc, + runRequest.expiration, + convertedResponse, + txOpts, + ]; +} + +/** + * Convert the javascript format of the parameters needed to call the + * ```solidity + * function cancelOracleRequest( + * bytes32 _requestId, + * uint256 _payment, + * bytes4 _callbackFunc, + * uint256 _expiration + * ) + * ``` + * method on an Oracle.sol contract. + * + * @param runRequest The run request to flatten into the correct order to perform the `cancelOracleRequest` function + * @param txOpts Additional ethereum tx options + */ +export function convertCancelParams( + runRequest: RunRequest, + txOpts: TxOptions = {}, +): [string, string, string, string, TxOptions] { + return [runRequest.requestId, runRequest.payment, runRequest.callbackFunc, runRequest.expiration, txOpts]; +} + +/** + * Abi encode parameters to call the `oracleRequest` method on the Oracle.sol contract. + * ```solidity + * function oracleRequest( + * address _sender, + * uint256 _payment, + * bytes32 _specId, + * address _callbackAddress, + * bytes4 _callbackFunctionId, + * uint256 _nonce, + * uint256 _dataVersion, + * bytes _data + * ) + * ``` + * + * @param specId The Job Specification ID + * @param callbackAddr The callback contract address for the response + * @param callbackFunctionId The callback function id for the response + * @param nonce The nonce sent by the requester + * @param data The CBOR payload of the request + */ +export function encodeOracleRequest( + specId: string, + callbackAddr: string, + callbackFunctionId: string, + nonce: number, + data: BigNumberish, + dataVersion: BigNumberish = 1, +): string { + const oracleRequestSighash = "0x40429946"; + return encodeRequest(oracleRequestSighash, specId, callbackAddr, callbackFunctionId, nonce, data, dataVersion); +} + +/** + * Abi encode parameters to call the `requestOracleData` method on the Operator.sol contract. + * ```solidity + * function requestOracleData( + * address _sender, + * uint256 _payment, + * bytes32 _specId, + * address _callbackAddress, + * bytes4 _callbackFunctionId, + * uint256 _nonce, + * uint256 _dataVersion, + * bytes _data + * ) + * ``` + * + * @param specId The Job Specification ID + * @param callbackAddr The callback contract address for the response + * @param callbackFunctionId The callback function id for the response + * @param nonce The nonce sent by the requester + * @param data The CBOR payload of the request + */ +export function encodeRequestOracleData( + specId: string, + callbackAddr: string, + callbackFunctionId: string, + nonce: number, + data: BigNumberish, + dataVersion: BigNumberish = 2, +): string { + const requestOracleDataSignhash = "0x6de879d6"; + return encodeRequest(requestOracleDataSignhash, specId, callbackAddr, callbackFunctionId, nonce, data, dataVersion); +} + +function encodeRequest( + oracleRequestSighash: string, + specId: string, + callbackAddr: string, + callbackFunctionId: string, + nonce: number, + data: BigNumberish, + dataVersion: BigNumberish = 1, +): string { + const oracleRequestInputs = [ + { name: "_sender", type: "address" }, + { name: "_payment", type: "uint256" }, + { name: "_specId", type: "bytes32" }, + { name: "_callbackAddress", type: "address" }, + { name: "_callbackFunctionId", type: "bytes4" }, + { name: "_nonce", type: "uint256" }, + { name: "_dataVersion", type: "uint256" }, + { name: "_data", type: "bytes" }, + ]; + const encodedParams = ethers.utils.defaultAbiCoder.encode( + oracleRequestInputs.map(i => i.type), + [ethers.constants.AddressZero, 0, specId, callbackAddr, callbackFunctionId, nonce, dataVersion, data], + ); + return `${oracleRequestSighash}${stripHexPrefix(encodedParams)}`; +} + +/** + * Extract a javascript representation of a run request from the data + * contained within a EVM log. + * ```solidity + * event OracleRequest( + * bytes32 indexed specId, + * address requester, + * bytes32 requestId, + * uint256 payment, + * address callbackAddr, + * bytes4 callbackFunctionId, + * uint256 cancelExpiration, + * uint256 dataVersion, + * bytes data + * ); + * ``` + * + * @param log The log to extract the run request from + */ +export function decodeRunRequest(log?: ethers.providers.Log): RunRequest { + if (!log) { + throw Error("No logs found to decode"); + } + + const ORACLE_REQUEST_TYPES = ["address", "bytes32", "uint256", "address", "bytes4", "uint256", "uint256", "bytes"]; + const [ + requester, + requestId, + payment, + callbackAddress, + callbackFunc, + expiration, + version, + data, + ] = ethers.utils.defaultAbiCoder.decode(ORACLE_REQUEST_TYPES, log.data); + + return { + specId: log.topics[1], + requester, + requestId: toHex(requestId), + payment: toHex(payment), + callbackAddr: callbackAddress, + callbackFunc: toHex(callbackFunc), + expiration: toHex(expiration), + data: addCBORMapDelimiters(Buffer.from(stripHexPrefix(data), "hex")), + dataVersion: version.toNumber(), + + topic: log.topics[0], + }; +} + +/** + * Extract a javascript representation of a ConcreteChainlinked#Request event + * from an EVM log. + * ```solidity + * event Request( + * bytes32 id, + * address callbackAddress, + * bytes4 callbackfunctionSelector, + * bytes data + * ); + * ``` + * The request event is emitted from the `ConcreteChainlinked.sol` testing contract. + * + * @param log The log to decode + */ +export function decodeCCRequest(log: ethers.providers.Log): ethers.utils.Result { + const d = debug.extend("decodeRunABI"); + d("params %o", log); + + const REQUEST_TYPES = ["bytes32", "address", "bytes4", "bytes"]; + const decodedValue = ethers.utils.defaultAbiCoder.decode(REQUEST_TYPES, log.data); + d("decoded value %o", decodedValue); + + return decodedValue; +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/test-helpers/setup.ts b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/setup.ts new file mode 100644 index 0000000..f8aaf8d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/test-helpers/setup.ts @@ -0,0 +1,85 @@ +import { ethers } from "hardhat"; +import { Signer } from "ethers"; + +export interface Contracts { + contract1: Signer; + contract2: Signer; + contract3: Signer; + contract4: Signer; + contract5: Signer; + contract6: Signer; + contract7: Signer; + contract8: Signer; +} + +export interface Roles { + defaultAccount: Signer; + oracleNode: Signer; + oracleNode1: Signer; + oracleNode2: Signer; + oracleNode3: Signer; + oracleNode4: Signer; + stranger: Signer; + consumer: Signer; +} + +export interface Personas { + Default: Signer; + Carol: Signer; + Eddy: Signer; + Nancy: Signer; + Ned: Signer; + Neil: Signer; + Nelly: Signer; + Norbert: Signer; +} + +export interface Users { + contracts: Contracts; + roles: Roles; + personas: Personas; +} + +export async function getUsers() { + let accounts = await ethers.getSigners(); + + const personas: Personas = { + Default: accounts[0], + Neil: accounts[1], + Ned: accounts[2], + Nelly: accounts[3], + Nancy: accounts[4], + Norbert: accounts[5], + Carol: accounts[6], + Eddy: accounts[7], + }; + + const contracts: Contracts = { + contract1: accounts[0], + contract2: accounts[1], + contract3: accounts[2], + contract4: accounts[3], + contract5: accounts[4], + contract6: accounts[5], + contract7: accounts[6], + contract8: accounts[7], + }; + + const roles: Roles = { + defaultAccount: accounts[0], + oracleNode: accounts[1], + oracleNode1: accounts[2], + oracleNode2: accounts[3], + oracleNode3: accounts[4], + oracleNode4: accounts[5], + stranger: accounts[6], + consumer: accounts[7], + }; + + const users: Users = { + personas, + roles, + contracts, + }; + return users; +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/Aggregator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Aggregator.test.ts new file mode 100644 index 0000000..2d62965 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Aggregator.test.ts @@ -0,0 +1,804 @@ +import { + contract, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { Aggregator__factory } from '../../ethers/v0.4/factories/Aggregator__factory' +import { Oracle__factory } from '../../ethers/v0.4/factories/Oracle__factory' + +const aggregatorFactory = new Aggregator__factory() +const oracleFactory = new Oracle__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +let personas: setup.Personas +let defaultAccount: ethers.Wallet +const provider = setup.provider() + +beforeAll(async () => { + const users = await setup.users(provider) + + personas = users.personas + defaultAccount = users.roles.defaultAccount +}) + +describe('Aggregator', () => { + const jobId1 = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000001' + const jobId2 = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000002' + const jobId3 = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000003' + const jobId4 = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000004' + const deposit = h.toWei('100') + const basePayment = h.toWei('1') + let link: contract.Instance + let rate: contract.Instance + let oc1: contract.Instance + let oc2: contract.Instance + let oc3: contract.Instance + let oc4: contract.Instance + let oracles: contract.Instance[] + let jobIds: string[] = [] + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(defaultAccount).deploy() + oc1 = await oracleFactory.connect(defaultAccount).deploy(link.address) + oc2 = await oracleFactory.connect(defaultAccount).deploy(link.address) + oc3 = await oracleFactory.connect(defaultAccount).deploy(link.address) + oc4 = await oracleFactory.connect(defaultAccount).deploy(link.address) + oracles = [oc1, oc2, oc3] + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(aggregatorFactory, [ + 'authorizedRequesters', + 'cancelRequest', + 'chainlinkCallback', + 'latestAnswer', + 'getAnswer', + 'destroy', + 'jobIds', + 'latestRound', + 'minimumResponses', + 'oracles', + 'paymentAmount', + 'requestRateUpdate', + 'setAuthorization', + 'transferLINK', + 'updateRequestDetails', + 'latestTimestamp', + 'getTimestamp', + // Ownable methods: + 'owner', + 'renounceOwnership', + 'transferOwnership', + ]) + }) + + describe('#requestRateUpdate', () => { + const response = h.numToBytes32(100) + + describe('basic updates', () => { + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + + await link.transfer(rate.address, deposit) + + const current = await rate.latestAnswer() + matchers.bigNum(ethers.constants.Zero, current) + }) + + it('emits a new round log', async () => { + const requestTx = await rate.requestRateUpdate() + const receipt = await requestTx.wait() + + const answerId = h.numToBytes32(1) + const newRoundLog = receipt.logs?.[receipt.logs.length - 1] + assert.equal(answerId, newRoundLog?.topics[1]) + }) + + it('trigger a request to the oracle and accepts a response', async () => { + const requestTx = await rate.requestRateUpdate() + const receipt = await requestTx.wait() + + const log = receipt.logs?.[3] + assert.equal(oc1.address, log?.address) + const request = oracle.decodeRunRequest(log) + + await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const current = await rate.latestAnswer() + + matchers.bigNum(response, current) + + const answerId = await rate.latestRound() + const currentMappingValue = await rate.getAnswer(answerId) + + matchers.bigNum(current, currentMappingValue) + }) + + it('change the updatedAt record', async () => { + let updatedAt = await rate.latestTimestamp() + assert.equal('0', updatedAt.toString()) + + const requestTx = await rate.requestRateUpdate() + const receipt = await requestTx.wait() + const request = oracle.decodeRunRequest(receipt.logs?.[3]) + await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + updatedAt = await rate.latestTimestamp() + assert.notEqual('0', updatedAt.toString()) + + const answerId = await rate.latestRound() + const timestampMappingValue = await rate.getTimestamp(answerId) + + matchers.bigNum(updatedAt, timestampMappingValue) + }) + + it('emits a log with the response, answer ID, and sender', async () => { + const requestTx = await rate.requestRateUpdate() + const requestTxreceipt = await requestTx.wait() + + const request = oracle.decodeRunRequest(requestTxreceipt.logs?.[3]) + const fulfillOracleRequest = await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + const fulfillOracleRequestReceipt = await fulfillOracleRequest.wait() + const answerId = h.numToBytes32(1) + + const receivedLog = fulfillOracleRequestReceipt.logs?.[1] + assert.equal(response, receivedLog?.topics?.[1]) + assert.equal(answerId, receivedLog?.topics?.[2]) + assert.equal( + oc1.address, + ethers.utils.getAddress( + receivedLog?.topics?.[3]?.slice(26, 66) ?? '', + ), + ) + }) + + it('emits a log with the new answer', async () => { + const requestTx = await rate.requestRateUpdate() + const requestReceipt = await requestTx.wait() + + const request = oracle.decodeRunRequest(requestReceipt.logs?.[3]) + const fulfillOracleRequest = await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + const fulfillOracleRequestReceipt = await fulfillOracleRequest.wait() + + const answerId = h.numToBytes32(1) + const answerUpdatedLog = fulfillOracleRequestReceipt.logs?.[2] + assert.equal(response, answerUpdatedLog?.topics[1]) + + assert.equal(answerId, answerUpdatedLog?.topics[2]) + }) + }) + + describe('with multiple oracles', () => { + beforeEach(async () => { + rate = await aggregatorFactory.connect(defaultAccount).deploy( + link.address, + basePayment, + oracles.length, + oracles.map((o) => o.address), + [jobId1, jobId2, jobId3], + ) + await link.transfer(rate.address, deposit) + + const current = await rate.latestAnswer() + matchers.bigNum(ethers.constants.Zero, current) + }) + + it('triggers requests to the oracles and the median of the responses', async () => { + const requestTx = await rate.requestRateUpdate() + const receipt = await requestTx.wait() + + const responses = [77, 66, 111].map(h.numToBytes32) + + for (let i = 0; i < oracles.length; i++) { + const o = oracles[i] + const log = receipt?.logs?.[i * 4 + 3] + assert.equal(o.address, log?.address) + const request = oracle.decodeRunRequest(log) + + await o.fulfillOracleRequest( + ...oracle.convertFufillParams(request, responses[i]), + ) + } + + const current = await rate.latestAnswer() + matchers.bigNum(h.numToBytes32(77), current) + + const answerId = await rate.latestRound() + const currentMappingValue = await rate.getAnswer(answerId) + + matchers.bigNum(current, currentMappingValue) + + const updatedAt = await rate.latestTimestamp() + assert.notEqual('0', updatedAt.toString()) + + const timestampMappingValue = await rate.getTimestamp(answerId) + + matchers.bigNum(updatedAt, timestampMappingValue) + }) + + it('does not accept old responses', async () => { + const request1 = await rate.requestRateUpdate() + const receipt1 = await request1.wait() + + const response1 = h.numToBytes32(100) + + const requests = [ + oracle.decodeRunRequest(receipt1.logs?.[3]), + oracle.decodeRunRequest(receipt1.logs?.[7]), + oracle.decodeRunRequest(receipt1.logs?.[11]), + ] + + const request2 = await rate.requestRateUpdate() + const receipt2 = await request2.wait() + const response2 = h.numToBytes32(200) + + for (let i = 0; i < oracles.length; i++) { + const log = receipt2.logs?.[i * 4 + 3] + const request = oracle.decodeRunRequest(log) + await oracles[i].fulfillOracleRequest( + ...oracle.convertFufillParams(request, response2), + ) + } + matchers.bigNum(response2, await rate.latestAnswer()) + + for (let i = 0; i < oracles.length; i++) { + await oracles[i].fulfillOracleRequest( + ...oracle.convertFufillParams(requests[i], response1), + ) + } + + matchers.bigNum(response2, await rate.latestAnswer()) + }) + }) + + describe('with an even number of oracles', () => { + beforeEach(async () => { + oracles = [oc1, oc2, oc3, oc4] + rate = await aggregatorFactory.connect(defaultAccount).deploy( + link.address, + basePayment, + oracles.length, + oracles.map((o) => o.address), + [jobId1, jobId2, jobId3, jobId4], + ) + + await link.transfer(rate.address, deposit) + + const current = await rate.latestAnswer() + matchers.bigNum(ethers.constants.Zero, current) + }) + + it('triggers requests to the oracles and the median of the responses', async () => { + const requestTx = await rate.requestRateUpdate() + const receipt = await requestTx.wait() + + const responses = [66, 76, 78, 111].map(h.numToBytes32) + + for (let i = 0; i < oracles.length; i++) { + const o = oracles[i] + const log = receipt.logs?.[i * 4 + 3] + assert.equal(o.address, log?.address) + const request = oracle.decodeRunRequest(log) + + await o.fulfillOracleRequest( + ...oracle.convertFufillParams(request, responses[i]), + ) + } + + const current = await rate.latestAnswer() + matchers.bigNum(77, current) + }) + }) + }) + + describe('#updateRequestDetails', () => { + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + await rate.transferOwnership(personas.Carol.address) + oc2 = await oracleFactory.connect(defaultAccount).deploy(link.address) + await link.transfer(rate.address, deposit) + + const current = await rate.latestAnswer() + matchers.bigNum(ethers.constants.Zero, current) + }) + + describe('when called by the owner', () => { + it('changes the amout of LINK sent on a request', async () => { + const uniquePayment = 7777777 + await rate + .connect(personas.Carol) + .updateRequestDetails(uniquePayment, 1, [oc2.address], [jobId2]) + + await rate.connect(personas.Carol).requestRateUpdate() + + matchers.bigNum(uniquePayment, await link.balanceOf(oc2.address)) + }) + + it('can be configured to accept fewer responses than oracles', async () => { + await rate + .connect(personas.Carol) + .updateRequestDetails( + basePayment, + 1, + [oc1.address, oc2.address], + [jobId1, jobId2], + ) + + const requestTx = await rate.connect(personas.Carol).requestRateUpdate() + const requestTxReceipt = await requestTx.wait() + const request1 = oracle.decodeRunRequest(requestTxReceipt.logs?.[3]) + const request2 = oracle.decodeRunRequest(requestTxReceipt.logs?.[7]) + + const response1 = h.numToBytes32(100) + await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request1, response1), + ) + matchers.bigNum(response1, await rate.latestAnswer()) + + const response2 = h.numToBytes32(200) + await oc2.fulfillOracleRequest( + ...oracle.convertFufillParams(request2, response2), + ) + + const response1Bn = ethers.utils.bigNumberify(response1) + const response2Bn = ethers.utils.bigNumberify(response2) + const expected = response1Bn.add(response2Bn).div(2) + + assert.isTrue(expected.eq(await rate.latestAnswer())) + }) + + describe('and the number of jobs does not match number of oracles', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate + .connect(personas.Carol) + .updateRequestDetails( + basePayment, + 2, + [oc1.address, oc2.address], + [jobId2], + ) + }) + }) + }) + + describe('and the oracles required exceeds the available amount', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate + .connect(personas.Carol) + .updateRequestDetails( + basePayment, + 3, + [oc1.address, oc2.address], + [jobId1, jobId2], + ) + }) + }) + }) + }) + + describe('when called by a non-owner', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate + .connect(personas.Eddy) + .updateRequestDetails(basePayment, 1, [oc2.address], [jobId2]) + }) + }) + }) + + describe('when called before a past answer is fulfilled', () => { + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + await link.transfer(rate.address, deposit) + + oc2 = await oracleFactory.connect(defaultAccount).deploy(link.address) + oc3 = await oracleFactory.connect(defaultAccount).deploy(link.address) + }) + + it('accepts answers from oracles at the time the request was made', async () => { + // make request 1 + const request1Tx = await rate.requestRateUpdate() + const request1Receipt = await request1Tx.wait() + const request1 = oracle.decodeRunRequest(request1Receipt.logs?.[3]) + + // change oracles + await rate.updateRequestDetails( + basePayment, + 2, + [oc2.address, oc3.address], + [jobId2, jobId3], + ) + + // make new request + const request2Tx = await rate.requestRateUpdate() + const request2Receipt = await request2Tx.wait() + const request2 = oracle.decodeRunRequest(request2Receipt.logs?.[3]) + const request3 = oracle.decodeRunRequest(request2Receipt.logs?.[7]) + + // fulfill request 1 + const response1 = h.numToBytes32(100) + await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request1, response1), + ) + matchers.bigNum(response1, await rate.latestAnswer()) + + // fulfill request 2 + const responses2 = [202, 222].map(h.numToBytes32) + await oc2.fulfillOracleRequest( + ...oracle.convertFufillParams(request2, responses2[0]), + ) + await oc3.fulfillOracleRequest( + ...oracle.convertFufillParams(request3, responses2[1]), + ) + matchers.bigNum(212, await rate.latestAnswer()) + }) + }) + + describe('when calling with a large number of oracles', () => { + const maxOracleCount = 28 + + beforeEach(() => { + oracles = [] + jobIds = [] + }) + + it(`does not revert with up to ${maxOracleCount} oracles`, async () => { + for (let i = 0; i < maxOracleCount; i++) { + oracles.push(oc1) + jobIds.push(jobId1) + } + assert.equal(maxOracleCount, oracles.length) + assert.equal(maxOracleCount, jobIds.length) + + await rate.connect(personas.Carol).updateRequestDetails( + basePayment, + maxOracleCount, + oracles.map((o) => o.address), + jobIds, + ) + }) + + it(`reverts with more than ${maxOracleCount} oracles`, async () => { + const overMaxOracles = maxOracleCount + 1 + + for (let i = 0; i < overMaxOracles; i++) { + oracles.push(oc1) + jobIds.push(jobId1) + } + assert.equal(overMaxOracles, oracles.length) + assert.equal(overMaxOracles, jobIds.length) + + await matchers.evmRevert(async () => { + await rate.connect(personas.Carol).updateRequestDetails( + basePayment, + overMaxOracles, + oracles.map((o) => o.address), + jobIds, + ) + }) + }) + }) + }) + + describe('#transferLINK', () => { + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + await rate.transferOwnership(personas.Carol.address) + await link.transfer(rate.address, deposit) + matchers.bigNum(deposit, await link.balanceOf(rate.address)) + }) + + describe('when called by the owner', () => { + it('succeeds', async () => { + await rate + .connect(personas.Carol) + .transferLINK(personas.Carol.address, deposit) + + matchers.bigNum(0, await link.balanceOf(rate.address)) + matchers.bigNum(deposit, await link.balanceOf(personas.Carol.address)) + }) + + describe('with a number higher than the LINK balance', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate + .connect(personas.Carol) + .transferLINK(personas.Carol.address, deposit.add(basePayment)) + }) + + matchers.bigNum(deposit, await link.balanceOf(rate.address)) + }) + }) + }) + + describe('when called by a non-owner', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate + .connect(personas.Eddy) + .transferLINK(personas.Carol.address, deposit) + }) + + matchers.bigNum(deposit, await link.balanceOf(rate.address)) + }) + }) + }) + + describe('#destroy', () => { + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + await rate.transferOwnership(personas.Carol.address) + await link.transfer(rate.address, deposit) + matchers.bigNum(deposit, await link.balanceOf(rate.address)) + }) + + describe('when called by the owner', () => { + it('succeeds', async () => { + await rate.connect(personas.Carol).destroy() + + matchers.bigNum(0, await link.balanceOf(rate.address)) + matchers.bigNum(deposit, await link.balanceOf(personas.Carol.address)) + + assert.equal('0x', await provider.getCode(rate.address)) + }) + }) + + describe('when called by a non-owner', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate.connect(personas.Eddy).destroy() + }) + + matchers.bigNum(deposit, await link.balanceOf(rate.address)) + assert.notEqual('0x', await provider.getCode(rate.address)) + }) + }) + }) + + describe('#setAuthorization', () => { + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + await link.transfer(rate.address, deposit) + }) + + describe('when called by an authorized address', () => { + beforeEach(async () => { + await rate.setAuthorization(personas.Eddy.address, true) + assert.equal( + true, + await rate.authorizedRequesters(personas.Eddy.address), + ) + }) + + it('succeeds', async () => { + await rate.connect(personas.Eddy).requestRateUpdate() + }) + + it('can be unset', async () => { + await rate.setAuthorization(personas.Eddy.address, false) + assert.equal( + false, + await rate.authorizedRequesters(personas.Eddy.address), + ) + + await matchers.evmRevert(async () => { + await rate.connect(personas.Eddy).requestRateUpdate() + }) + }) + }) + + describe('when called by a non-authorized address', () => { + beforeEach(async () => { + assert.equal( + false, + await rate.authorizedRequesters(personas.Eddy.address), + ) + }) + + it('fails', async () => { + await matchers.evmRevert(async () => { + await rate.connect(personas.Eddy).requestRateUpdate() + }) + }) + }) + }) + + describe('#cancelRequest', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 1, [oc1.address], [jobId1]) + + await link.transfer(rate.address, basePayment) + + matchers.bigNum(basePayment, await link.balanceOf(rate.address)) + matchers.bigNum(0, await link.balanceOf(oc1.address)) + + const requestTx = await rate.requestRateUpdate() + const receipt = await requestTx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + + matchers.bigNum(0, await link.balanceOf(rate.address)) + matchers.bigNum(basePayment, await link.balanceOf(oc1.address)) + + await h.increaseTime5Minutes(provider) // wait for request to expire + }) + + describe('when a later answer has been provided', () => { + beforeEach(async () => { + await link.transfer(rate.address, basePayment) + const requestTx2 = await rate.requestRateUpdate() + const receipt = await requestTx2.wait() + const request2 = oracle.decodeRunRequest(receipt.logs?.[3]) + await oc1.fulfillOracleRequest( + ...oracle.convertFufillParams(request2, '17'), + ) + + matchers.bigNum(basePayment.mul(2), await link.balanceOf(oc1.address)) + }) + + it('gets the LINK deposited back from the oracle', async () => { + await rate.cancelRequest( + request.requestId, + request.payment, + request.expiration, + ) + + matchers.bigNum(basePayment, await link.balanceOf(rate.address)) + matchers.bigNum(basePayment, await link.balanceOf(oc1.address)) + }) + }) + + describe('when a later answer has not been provided', () => { + it('does not allow the request to be cancelled', async () => { + await matchers.evmRevert(async () => { + await rate.cancelRequest( + request.requestId, + request.payment, + request.expiration, + ) + }) + + matchers.bigNum(0, await link.balanceOf(rate.address)) + matchers.bigNum(basePayment, await link.balanceOf(oc1.address)) + }) + }) + }) + + describe('testing various sets of inputs', () => { + const tests = [ + { + name: 'ordered ascending', + responses: [0, 1, 2, 3, 4, 5, 6, 7].map(h.numToBytes32), + want: h.numToBytes32(3), + }, + { + name: 'ordered descending', + responses: [7, 6, 5, 4, 3, 2, 1, 0].map(h.numToBytes32), + want: h.numToBytes32(3), + }, + { + name: 'unordered 1', + responses: [1001, 1, 101, 10, 11, 0, 111].map(h.numToBytes32), + want: h.numToBytes32(11), + }, + { + name: 'unordered 2', + responses: [8, 8, 4, 5, 5, 7, 9, 5, 9].map(h.numToBytes32), + want: h.numToBytes32(7), + }, + { + name: 'unordered 3', + responses: [33, 44, 89, 101, 67, 7, 23, 55, 88, 324, 0, 88].map( + h.numToBytes32, + ), + want: h.numToBytes32(61), // 67 + 55 / 2 + }, + { + name: 'long unordered', + responses: [ + 333121, + 323453, + 337654, + 345363, + 345363, + 333456, + 335477, + 333323, + 332352, + 354648, + 983260, + 333856, + 335468, + 376987, + 333253, + 388867, + 337879, + 333324, + 338678, + ].map(h.numToBytes32), + want: h.numToBytes32(335477), + }, + ] + + beforeEach(async () => { + rate = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, basePayment, 0, [], []) + await link.transfer(rate.address, deposit) + }) + + for (const test of tests) { + const responses = test.responses + const oracles: contract.Instance[] = [] + const jobIds: string[] = [] + + it(test.name, async () => { + for (let i = 0; i < responses.length; i++) { + oracles[i] = await oracleFactory + .connect(defaultAccount) + .deploy(link.address) + jobIds[i] = jobId1 // doesn't really matter in this test + } + + await rate.updateRequestDetails( + basePayment, + oracles.length, + oracles.map((o) => o.address), + jobIds, + ) + + const requestTx = await rate.requestRateUpdate() + + for (let i = 0; i < responses.length; i++) { + const o = oracles[i] + const receipt = await requestTx.wait() + const log = receipt.logs?.[i * 4 + 3] + assert.equal(o.address, log?.address) + const request = oracle.decodeRunRequest(log) + + await o.fulfillOracleRequest( + ...oracle.convertFufillParams(request, responses[i]), + ) + } + + matchers.bigNum(test.want, await rate.latestAnswer()) + }) + } + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/BasicConsumer.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/BasicConsumer.test.ts new file mode 100644 index 0000000..01b4d6d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/BasicConsumer.test.ts @@ -0,0 +1,231 @@ +import { + contract, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import cbor from 'cbor' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { BasicConsumer__factory } from '../../ethers/v0.4/factories/BasicConsumer__factory' +import { Oracle__factory } from '../../ethers/v0.4/factories/Oracle__factory' + +const basicConsumerFactory = new BasicConsumer__factory() +const oracleFactory = new Oracle__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +// create ethers provider from that web3js instance +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('BasicConsumer', () => { + const specId = '0x4c7b7ffb66b344fbaa64995af81e355a'.padEnd(66, '0') + const currency = 'USD' + let link: contract.Instance + let oc: contract.Instance + let cc: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + oc = await oracleFactory.connect(roles.oracleNode).deploy(link.address) + cc = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address, specId) + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a predictable gas price', async () => { + const rec = await provider.getTransactionReceipt( + cc?.deployTransaction?.hash ?? '', + ) + assert.isBelow(rec?.gasUsed?.toNumber() ?? 0, 1700000) + }) + + describe('#requestEthereumPrice', () => { + describe('without LINK', () => { + it('reverts', async () => { + await matchers.evmRevert(async () => { + await cc.requestEthereumPrice(currency) + }) + }) + }) + + describe('with LINK', () => { + beforeEach(async () => { + await link.transfer(cc.address, ethers.utils.parseEther('1')) + }) + + it('triggers a log event in the Oracle contract', async () => { + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + const log = receipt?.logs?.[3] + assert.equal(log?.address ?? '', oc.address) + + const request = oracle.decodeRunRequest(log) + const expected = { + path: ['USD'], + get: + 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY', + } + + assert.equal(h.toHex(specId), request.specId) + matchers.bigNum(h.toWei('1'), request.payment) + assert.equal(cc.address, request.requester) + assert.equal(1, request.dataVersion) + assert.deepEqual(expected, cbor.decodeFirstSync(request.data)) + }) + + it('has a reasonable gas cost', async () => { + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + assert.isBelow(receipt.gasUsed?.toNumber() ?? 0, 130000) + }) + }) + }) + + describe('#fulfillOracleRequest', () => { + const response = ethers.utils.formatBytes32String('1,000,000.00') + let request: oracle.RunRequest + + beforeEach(async () => { + await link.transfer(cc.address, h.toWei('1')) + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + }) + + it('records the data given to it by the oracle', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest(...oracle.convertFufillParams(request, response)) + + const currentPrice = await cc.currentPrice() + assert.equal(currentPrice, response) + }) + + it('logs the data given to it by the oracle', async () => { + const tx = await oc + .connect(roles.oracleNode) + .fulfillOracleRequest(...oracle.convertFufillParams(request, response)) + const receipt = await tx.wait() + assert.equal(2, receipt.logs?.length) + const log = receipt.logs?.[1] + + assert.equal(log?.topics[2], response) + }) + + describe('when the consumer does not recognize the request ID', () => { + let otherRequest: oracle.RunRequest + + beforeEach(async () => { + const args = oracle.encodeOracleRequest( + specId, + cc.address, + basicConsumerFactory.interface.functions.fulfill.sighash, + 43, + '0x0', + ) + const tx = await link.transferAndCall(oc.address, 0, args) + const receipt = await tx.wait() + otherRequest = oracle.decodeRunRequest(receipt.logs?.[2]) + }) + + it('does not accept the data provided', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(otherRequest, response), + ) + + const received = await cc.currentPrice() + + assert.equal(ethers.utils.parseBytes32String(received), '') + }) + }) + + describe('when called by anyone other than the oracle contract', () => { + it('does not accept the data provided', async () => { + await matchers.evmRevert(async () => { + await cc + .connect(roles.oracleNode) + .fulfill(request.requestId, response) + }) + + const received = await cc.currentPrice() + assert.equal(ethers.utils.parseBytes32String(received), '') + }) + }) + }) + + describe('#cancelRequest', () => { + const depositAmount = h.toWei('1') + let request: oracle.RunRequest + + beforeEach(async () => { + await link.transfer(cc.address, depositAmount) + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + }) + + describe('before 5 minutes', () => { + it('cant cancel the request', async () => { + await matchers.evmRevert(async () => { + await cc + .connect(roles.consumer) + .cancelRequest( + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ) + }) + }) + }) + + describe('after 5 minutes', () => { + it('can cancel the request', async () => { + await h.increaseTime5Minutes(provider) + + await cc + .connect(roles.consumer) + .cancelRequest( + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ) + }) + }) + }) + + describe('#withdrawLink', () => { + const depositAmount = h.toWei('1') + + beforeEach(async () => { + await link.transfer(cc.address, depositAmount) + const balance = await link.balanceOf(cc.address) + matchers.bigNum(balance, depositAmount) + }) + + it('transfers LINK out of the contract', async () => { + await cc.connect(roles.consumer).withdrawLink() + const ccBalance = await link.balanceOf(cc.address) + const consumerBalance = ethers.utils.bigNumberify( + await link.balanceOf(roles.consumer.address), + ) + matchers.bigNum(ccBalance, 0) + matchers.bigNum(consumerBalance, depositAmount) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/Chainlinked.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Chainlinked.test.ts new file mode 100644 index 0000000..1f1d404 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Chainlinked.test.ts @@ -0,0 +1,10 @@ +import { matchers } from '@chainlink/test-helpers' +import { Chainlinked__factory } from '../../ethers/v0.4/factories/Chainlinked__factory' + +const chainlinkedFactory = new Chainlinked__factory() + +describe('Chainlinked', () => { + it('has a limited public interface', async () => { + matchers.publicAbi(chainlinkedFactory, []) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/ConcreteChainlink.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/ConcreteChainlink.test.ts new file mode 100644 index 0000000..ad58dbd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/ConcreteChainlink.test.ts @@ -0,0 +1,186 @@ +import { + contract, + debug as d, + helpers as h, + matchers, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { ConcreteChainlink__factory } from '../../ethers/v0.4/factories/ConcreteChainlink__factory' + +const provider = setup.provider() +const concreteChainlinkFactory = new ConcreteChainlink__factory() +const debug = d.makeDebug('ConcreteChainlink') + +describe('ConcreteChainlink', () => { + let ccl: contract.Instance + let defaultAccount: ethers.Wallet + const deployment = setup.snapshot(provider, async () => { + defaultAccount = await setup + .users(provider) + .then((r) => r.roles.defaultAccount) + ccl = await concreteChainlinkFactory.connect(defaultAccount).deploy() + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(concreteChainlinkFactory, [ + 'add', + 'addBytes', + 'addInt', + 'addStringArray', + 'addUint', + 'closeEvent', + 'setBuffer', + ]) + }) + + async function parseCCLEvent(tx: ethers.providers.TransactionResponse) { + const receipt = await tx.wait() + const data = receipt.logs?.[0].data + const d = debug.extend('parseCCLEvent') + d('data %s', data) + return ethers.utils.defaultAbiCoder.decode(['bytes'], data ?? '') + } + + describe('#close', () => { + it('handles empty payloads', async () => { + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + assert.deepEqual(decoded, {}) + }) + }) + + describe('#setBuffer', () => { + it('emits the buffer', async () => { + await ccl.setBuffer('0xA161616162') + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + assert.deepEqual(decoded, { a: 'b' }) + }) + }) + + describe('#add', () => { + it('stores and logs keys and values', async () => { + await ccl.add('first', 'word!!') + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + assert.deepEqual(decoded, { first: 'word!!' }) + }) + + it('handles two entries', async () => { + await ccl.add('first', 'uno') + await ccl.add('second', 'dos') + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + + assert.deepEqual(decoded, { + first: 'uno', + second: 'dos', + }) + }) + }) + + describe('#addBytes', () => { + it('stores and logs keys and values', async () => { + await ccl.addBytes('first', '0xaabbccddeeff') + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + const expected = h.hexToBuf('0xaabbccddeeff') + assert.deepEqual(decoded, { first: expected }) + }) + + it('handles two entries', async () => { + await ccl.addBytes('first', '0x756E6F') + await ccl.addBytes('second', '0x646F73') + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + + const expectedFirst = h.hexToBuf('0x756E6F') + const expectedSecond = h.hexToBuf('0x646F73') + assert.deepEqual(decoded, { + first: expectedFirst, + second: expectedSecond, + }) + }) + + it('handles strings', async () => { + await ccl.addBytes('first', ethers.utils.toUtf8Bytes('apple')) + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + const expected = ethers.utils.toUtf8Bytes('apple') + assert.deepEqual(decoded, { first: expected }) + }) + }) + + describe('#addInt', () => { + it('stores and logs keys and values', async () => { + await ccl.addInt('first', 1) + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + assert.deepEqual(decoded, { first: 1 }) + }) + + it('handles two entries', async () => { + await ccl.addInt('first', 1) + await ccl.addInt('second', 2) + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + + assert.deepEqual(decoded, { + first: 1, + second: 2, + }) + }) + }) + + describe('#addUint', () => { + it('stores and logs keys and values', async () => { + await ccl.addUint('first', 1) + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + assert.deepEqual(decoded, { first: 1 }) + }) + + it('handles two entries', async () => { + await ccl.addUint('first', 1) + await ccl.addUint('second', 2) + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + + assert.deepEqual(decoded, { + first: 1, + second: 2, + }) + }) + }) + + describe('#addStringArray', () => { + it('stores and logs keys and values', async () => { + await ccl.addStringArray('word', [ + ethers.utils.formatBytes32String('seinfeld'), + ethers.utils.formatBytes32String('"4"'), + ethers.utils.formatBytes32String('LIFE'), + ]) + const tx = await ccl.closeEvent() + const [payload] = await parseCCLEvent(tx) + const decoded = await h.decodeDietCBOR(payload) + assert.deepEqual(decoded, { word: ['seinfeld', '"4"', 'LIFE'] }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/ConcreteChainlinked.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/ConcreteChainlinked.test.ts new file mode 100644 index 0000000..30de105 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/ConcreteChainlinked.test.ts @@ -0,0 +1,296 @@ +import { + contract, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { ConcreteChainlinked__factory } from '../../ethers/v0.4/factories/ConcreteChainlinked__factory' +import { EmptyOracle__factory } from '../../ethers/v0.4/factories/EmptyOracle__factory' +import { GetterSetter__factory } from '../../ethers/v0.4/factories/GetterSetter__factory' +import { Oracle__factory } from '../../ethers/v0.4/factories/Oracle__factory' + +const concreteChainlinkedFactory = new ConcreteChainlinked__factory() +const emptyOracleFactory = new EmptyOracle__factory() +const getterSetterFactory = new GetterSetter__factory() +const oracleFactory = new Oracle__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('ConcreteChainlinked', () => { + const specId = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' + let cc: contract.Instance + let gs: contract.Instance + let oc: contract.Instance + let newoc: contract.Instance + let link: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + oc = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) + newoc = await oracleFactory + .connect(roles.defaultAccount) + .deploy(link.address) + gs = await getterSetterFactory.connect(roles.defaultAccount).deploy() + cc = await concreteChainlinkedFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address) + }) + + beforeEach(async () => { + await deployment() + }) + + describe('#newRequest', () => { + it('forwards the information to the oracle contract through the link token', async () => { + const tx = await cc.publicNewRequest( + specId, + gs.address, + ethers.utils.toUtf8Bytes('requestedBytes32(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + + assert.equal(1, receipt.logs?.length) + const [jId, cbAddr, cbFId, cborData] = receipt.logs + ? oracle.decodeCCRequest(receipt.logs[0]) + : [] + const params = h.decodeDietCBOR(cborData ?? '') + + assert.equal(specId, jId) + assert.equal(gs.address, cbAddr) + assert.equal('0xed53e511', cbFId) + assert.deepEqual({}, params) + }) + }) + + describe('#chainlinkRequest(Request)', () => { + it('emits an event from the contract showing the run ID', async () => { + const tx = await cc.publicRequest( + specId, + cc.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + + const { events, logs } = await tx.wait() + + assert.equal(4, events?.length) + + assert.equal(logs?.[0].address, cc.address) + assert.equal(events?.[0].event, 'ChainlinkRequested') + }) + }) + + describe('#chainlinkRequestTo(Request)', () => { + it('emits an event from the contract showing the run ID', async () => { + const tx = await cc.publicRequestRunTo( + newoc.address, + specId, + cc.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + const { events } = await tx.wait() + + assert.equal(4, events?.length) + assert.equal(events?.[0].event, 'ChainlinkRequested') + }) + + it('emits an event on the target oracle contract', async () => { + const tx = await cc.publicRequestRunTo( + newoc.address, + specId, + cc.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + const { logs } = await tx.wait() + const event = logs && newoc.interface.parseLog(logs[3]) + + assert.equal(4, logs?.length) + assert.equal(event?.name, 'OracleRequest') + }) + + it('does not modify the stored oracle address', async () => { + await cc.publicRequestRunTo( + newoc.address, + specId, + cc.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + + const actualOracleAddress = await cc.publicOracleAddress() + assert.equal(oc.address, actualOracleAddress) + }) + }) + + describe('#cancelChainlinkRequest', () => { + let requestId: string + // a concrete chainlink attached to an empty oracle + let ecc: contract.Instance + + beforeEach(async () => { + const emptyOracle = await emptyOracleFactory + .connect(roles.defaultAccount) + .deploy() + ecc = await concreteChainlinkedFactory + .connect(roles.defaultAccount) + .deploy(link.address, emptyOracle.address) + + const tx = await ecc.publicRequest( + specId, + ecc.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + const { events } = await tx.wait() + requestId = (events?.[0]?.args as any).id + }) + + it('emits an event from the contract showing the run was cancelled', async () => { + const tx = await ecc.publicCancelRequest( + requestId, + 0, + ethers.utils.hexZeroPad('0x', 4), + 0, + ) + const { events } = await tx.wait() + + assert.equal(1, events?.length) + assert.equal(events?.[0].event, 'ChainlinkCancelled') + assert.equal(requestId, (events?.[0].args as any).id) + }) + + it('throws if given a bogus event ID', async () => { + await matchers.evmRevert(async () => { + await ecc.publicCancelRequest( + ethers.utils.formatBytes32String('bogusId'), + 0, + ethers.utils.hexZeroPad('0x', 4), + 0, + ) + }) + }) + }) + + describe('#recordChainlinkFulfillment(modifier)', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + const tx = await cc.publicRequest( + specId, + cc.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + const { logs } = await tx.wait() + + request = oracle.decodeRunRequest(logs?.[3]) + }) + + it('emits an event marking the request fulfilled', async () => { + const tx = await oc.fulfillOracleRequest( + ...oracle.convertFufillParams( + request, + ethers.utils.formatBytes32String('hi mom!'), + ), + ) + const { logs } = await tx.wait() + + const event = logs && cc.interface.parseLog(logs[0]) + + assert.equal(1, logs?.length) + assert.equal(event?.name, 'ChainlinkFulfilled') + assert.equal(request.requestId, event?.values.id) + }) + }) + + describe('#fulfillChainlinkRequest(function)', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + const tx = await cc.publicRequest( + specId, + cc.address, + ethers.utils.toUtf8Bytes( + 'publicFulfillChainlinkRequest(bytes32,bytes32)', + ), + 0, + ) + const { logs } = await tx.wait() + + request = oracle.decodeRunRequest(logs?.[3]) + }) + + it('emits an event marking the request fulfilled', async () => { + const tx = await oc.fulfillOracleRequest( + ...oracle.convertFufillParams( + request, + ethers.utils.formatBytes32String('hi mom!'), + ), + ) + const { logs } = await tx.wait() + const event = logs && cc.interface.parseLog(logs[0]) + + assert.equal(1, logs?.length) + assert.equal(event?.name, 'ChainlinkFulfilled') + assert.equal(request.requestId, event?.values?.id) + }) + }) + + describe('#chainlinkToken', () => { + it('returns the Link Token address', async () => { + const addr = await cc.publicChainlinkToken() + assert.equal(addr, link.address) + }) + }) + + describe('#addExternalRequest', () => { + let mock: contract.Instance + let request: oracle.RunRequest + + beforeEach(async () => { + mock = await concreteChainlinkedFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address) + + const tx = await cc.publicRequest( + specId, + mock.address, + ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), + 0, + ) + const receipt = await tx.wait() + + request = oracle.decodeRunRequest(receipt.logs?.[3]) + await mock.publicAddExternalRequest(oc.address, request.requestId) + }) + + it('allows the external request to be fulfilled', async () => { + await oc.fulfillOracleRequest( + ...oracle.convertFufillParams( + request, + ethers.utils.formatBytes32String('hi mom!'), + ), + ) + }) + + it('does not allow the same requestId to be used', async () => { + await matchers.evmRevert(async () => { + await cc.publicAddExternalRequest(newoc.address, request.requestId) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/GetterSetter.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/GetterSetter.test.ts new file mode 100644 index 0000000..324e885 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/GetterSetter.test.ts @@ -0,0 +1,109 @@ +import { contract, setup } from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { GetterSetter__factory } from '../../ethers/v0.4/factories/GetterSetter__factory' + +const GetterSetterContract = new GetterSetter__factory() +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('GetterSetter', () => { + const requestId = + '0x3bd198932d9cc01e2950ffc518fd38a303812200000000000000000000000000' + const bytes32 = ethers.utils.formatBytes32String('Hi Mom!') + const uint256 = ethers.utils.bigNumberify(645746535432) + let gs: contract.Instance + const deployment = setup.snapshot(provider, async () => { + gs = await GetterSetterContract.connect(roles.defaultAccount).deploy() + }) + + beforeEach(async () => { + await deployment() + }) + + describe('#setBytes32Val', () => { + it('updates the bytes32 value', async () => { + await gs.connect(roles.stranger).setBytes32(bytes32) + + const currentBytes32 = await gs.getBytes32() + assert.equal( + ethers.utils.toUtf8String(currentBytes32), + ethers.utils.toUtf8String(bytes32), + ) + }) + + it('logs an event', async () => { + const tx = await gs.connect(roles.stranger).setBytes32(bytes32) + + const receipt = await tx.wait() + const args: any = receipt.events?.[0].args + + assert.equal(1, receipt.events?.length) + assert.equal( + roles.stranger.address.toLowerCase(), + args.from.toLowerCase(), + ) + assert.equal( + ethers.utils.toUtf8String(bytes32), + ethers.utils.toUtf8String(args.value), + ) + }) + }) + + describe('#requestedBytes32', () => { + it('updates the request ID and value', async () => { + await gs.connect(roles.stranger).requestedBytes32(requestId, bytes32) + + const currentRequestId = await gs.requestId() + assert.equal(currentRequestId, requestId) + + const currentBytes32 = await gs.getBytes32() + assert.equal( + ethers.utils.toUtf8String(currentBytes32), + ethers.utils.toUtf8String(bytes32), + ) + }) + }) + + describe('#setUint256', () => { + it('updates uint256 value', async () => { + await gs.connect(roles.stranger).setUint256(uint256) + + const currentUint256 = await gs.getUint256() + assert.isTrue(currentUint256.eq(uint256)) + }) + + it('logs an event', async () => { + const tx = await gs.connect(roles.stranger).setUint256(uint256) + const receipt = await tx.wait() + const args: any = receipt.events?.[0].args + + assert.equal(1, receipt.events?.length) + assert.equal( + roles.stranger.address.toLowerCase(), + args.from.toLowerCase(), + ) + + assert.isTrue(uint256.eq(args.value)) + }) + }) + + describe('#requestedUint256', () => { + it('updates the request ID and value', async () => { + await gs.connect(roles.stranger).requestedUint256(requestId, uint256) + + const currentRequestId = await gs.requestId() + assert.equal(currentRequestId, requestId) + + const currentUint256 = await gs.getUint256() + assert.isTrue(currentUint256.eq(uint256)) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/Oracle.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Oracle.test.ts new file mode 100644 index 0000000..5e16034 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Oracle.test.ts @@ -0,0 +1,918 @@ +import { + contract, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { BasicConsumer__factory } from '../../ethers/v0.4/factories/BasicConsumer__factory' +import { GetterSetter__factory } from '../../ethers/v0.4/factories/GetterSetter__factory' +import { MaliciousConsumer__factory } from '../../ethers/v0.4/factories/MaliciousConsumer__factory' +import { MaliciousRequester__factory } from '../../ethers/v0.4/factories/MaliciousRequester__factory' +import { Oracle__factory } from '../../ethers/v0.4/factories/Oracle__factory' + +const basicConsumerFactory = new BasicConsumer__factory() +const getterSetterFactory = new GetterSetter__factory() +const maliciousRequesterFactory = new MaliciousRequester__factory() +const maliciousConsumerFactory = new MaliciousConsumer__factory() +const oracleFactory = new Oracle__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +let roles: setup.Roles +const provider = setup.provider() + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('Oracle', () => { + const fHash = getterSetterFactory.interface.functions.requestedBytes32.sighash + const specId = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' + const to = '0x80e29acb842498fe6591f020bd82766dce619d43' + let link: contract.Instance + let oc: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + oc = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) + await oc.setFulfillmentPermission(roles.oracleNode.address, true) + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(oracleFactory, [ + 'EXPIRY_TIME', + 'cancelOracleRequest', + 'fulfillOracleRequest', + 'getAuthorizationStatus', + 'onTokenTransfer', + 'oracleRequest', + 'setFulfillmentPermission', + 'withdraw', + 'withdrawable', + // Ownable methods: + 'owner', + 'renounceOwnership', + 'transferOwnership', + ]) + }) + + describe('#setFulfillmentPermission', () => { + describe('when called by the owner', () => { + beforeEach(async () => { + await oc + .connect(roles.defaultAccount) + .setFulfillmentPermission(roles.stranger.address, true) + }) + + it('adds an authorized node', async () => { + const authorized = await oc.getAuthorizationStatus( + roles.stranger.address, + ) + assert.equal(true, authorized) + }) + + it('removes an authorized node', async () => { + await oc + .connect(roles.defaultAccount) + .setFulfillmentPermission(roles.stranger.address, false) + const authorized = await oc.getAuthorizationStatus( + roles.stranger.address, + ) + assert.equal(false, authorized) + }) + }) + + describe('when called by a non-owner', () => { + it('cannot add an authorized node', async () => { + await matchers.evmRevert(async () => { + await oc + .connect(roles.stranger) + .setFulfillmentPermission(roles.stranger.address, true) + }) + }) + }) + }) + + describe('#onTokenTransfer', () => { + describe('when called from any address but the LINK token', () => { + it('triggers the intended method', async () => { + const callData = oracle.encodeOracleRequest(specId, to, fHash, 0, '0x0') + + await matchers.evmRevert(async () => { + await oc.onTokenTransfer(roles.defaultAccount.address, 0, callData) + }) + }) + }) + + describe('when called from the LINK token', () => { + it('triggers the intended method', async () => { + const callData = oracle.encodeOracleRequest(specId, to, fHash, 0, '0x0') + + const tx = await link.transferAndCall(oc.address, 0, callData, { + value: 0, + }) + const receipt = await tx.wait() + + assert.equal(3, receipt.logs?.length) + }) + + describe('with no data', () => { + it('reverts', async () => { + await matchers.evmRevert(async () => { + await link.transferAndCall(oc.address, 0, '0x', { + value: 0, + }) + }) + }) + }) + }) + + describe('malicious requester', () => { + let mock: contract.Instance + let requester: contract.Instance + const paymentAmount = h.toWei('1') + + beforeEach(async () => { + mock = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address) + await link.transfer(mock.address, paymentAmount) + }) + + it('cannot withdraw from oracle', async () => { + const ocOriginalBalance = await link.balanceOf(oc.address) + const mockOriginalBalance = await link.balanceOf(mock.address) + + await matchers.evmRevert(async () => { + await mock.maliciousWithdraw() + }) + + const ocNewBalance = await link.balanceOf(oc.address) + const mockNewBalance = await link.balanceOf(mock.address) + + matchers.bigNum(ocOriginalBalance, ocNewBalance) + matchers.bigNum(mockNewBalance, mockOriginalBalance) + }) + + describe('if the requester tries to create a requestId for another contract', () => { + it('the requesters ID will not match with the oracle contract', async () => { + const tx = await mock.maliciousTargetConsumer(to) + const receipt = await tx.wait() + + const mockRequestId = receipt.logs?.[0].data + const requestId = (receipt.events?.[0].args as any).requestId + assert.notEqual(mockRequestId, requestId) + }) + + it('the target requester can still create valid requests', async () => { + requester = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address, specId) + await link.transfer(requester.address, paymentAmount) + await mock.maliciousTargetConsumer(requester.address) + await requester.requestEthereumPrice('USD') + }) + }) + }) + + it('does not allow recursive calls of onTokenTransfer', async () => { + const requestPayload = oracle.encodeOracleRequest( + specId, + to, + fHash, + 0, + '0x0', + ) + + const ottSelector = + oracleFactory.interface.functions.onTokenTransfer.sighash + const header = + '000000000000000000000000c5fdf4076b8f3a5357c5e395ab970b5b54098fef' + // to + '0000000000000000000000000000000000000000000000000000000000000539' + // amount + '0000000000000000000000000000000000000000000000000000000000000060' + // offset + '0000000000000000000000000000000000000000000000000000000000000136' // length + + const maliciousPayload = ottSelector + header + requestPayload.slice(2) + + await matchers.evmRevert(async () => { + await link.transferAndCall(oc.address, 0, maliciousPayload, { + value: 0, + }) + }) + }) + }) + + describe('#oracleRequest', () => { + describe('when called through the LINK token', () => { + const paid = 100 + let log: ethers.providers.Log | undefined + let receipt: ethers.providers.TransactionReceipt + + beforeEach(async () => { + const args = oracle.encodeOracleRequest(specId, to, fHash, 1, '0x0') + const tx = await link.transferAndCall(oc.address, paid, args) + receipt = await tx.wait() + assert.equal(3, receipt?.logs?.length) + + log = receipt.logs && receipt.logs[2] + }) + + it('logs an event', async () => { + assert.equal(oc.address, log?.address) + + assert.equal(log?.topics?.[1], specId) + + const req = oracle.decodeRunRequest(receipt?.logs?.[2]) + assert.equal(roles.defaultAccount.address, req.requester) + matchers.bigNum(paid, req.payment) + }) + + it('uses the expected event signature', async () => { + // If updating this test, be sure to update models.RunLogTopic. + const eventSignature = + '0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65' + assert.equal(eventSignature, log?.topics?.[0]) + }) + + it('does not allow the same requestId to be used twice', async () => { + const args2 = oracle.encodeOracleRequest(specId, to, fHash, 1, '0x0') + await matchers.evmRevert(async () => { + await link.transferAndCall(oc.address, paid, args2) + }) + }) + + describe('when called with a payload less than 2 EVM words + function selector', () => { + const funcSelector = + oracleFactory.interface.functions.oracleRequest.sighash + const maliciousData = + funcSelector + + '0000000000000000000000000000000000000000000000000000000000000000000' + + it('throws an error', async () => { + await matchers.evmRevert(async () => { + await link.transferAndCall(oc.address, paid, maliciousData) + }) + }) + }) + + describe('when called with a payload between 3 and 9 EVM words', () => { + const funcSelector = + oracleFactory.interface.functions.oracleRequest.sighash + const maliciousData = + funcSelector + + '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' + + it('throws an error', async () => { + await matchers.evmRevert(async () => { + await link.transferAndCall(oc.address, paid, maliciousData) + }) + }) + }) + }) + + describe('when not called through the LINK token', () => { + it('reverts', async () => { + await matchers.evmRevert(async () => { + await oc + .connect(roles.oracleNode) + .oracleRequest( + '0x0000000000000000000000000000000000000000', + 0, + specId, + to, + fHash, + 1, + 1, + '0x', + ) + }) + }) + }) + }) + + describe('#fulfillOracleRequest', () => { + const response = 'Hi Mom!' + let maliciousRequester: contract.Instance + let basicConsumer: contract.Instance + let maliciousConsumer: contract.Instance + let request: ReturnType + + describe('cooperative consumer', () => { + beforeEach(async () => { + basicConsumer = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address, specId) + const paymentAmount = h.toWei('1') + await link.transfer(basicConsumer.address, paymentAmount) + const currency = 'USD' + const tx = await basicConsumer.requestEthereumPrice(currency) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + }) + + describe('when called by an unauthorized node', () => { + beforeEach(async () => { + assert.equal( + false, + await oc.getAuthorizationStatus(roles.stranger.address), + ) + }) + + it('raises an error', async () => { + await matchers.evmRevert(async () => { + await oc + .connect(roles.stranger) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + }) + }) + }) + + describe('when called by an authorized node', () => { + it('raises an error if the request ID does not exist', async () => { + request.requestId = ethers.utils.formatBytes32String('DOESNOTEXIST') + await matchers.evmRevert(async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + }) + }) + + it('sets the value on the requested contract', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const currentValue = await basicConsumer.currentPrice() + assert.equal(response, ethers.utils.parseBytes32String(currentValue)) + }) + + it('does not allow a request to be fulfilled twice', async () => { + const response2 = response + ' && Hello World!!' + + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + await matchers.evmRevert(async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response2), + ) + }) + + const currentValue = await basicConsumer.currentPrice() + assert.equal(response, ethers.utils.parseBytes32String(currentValue)) + }) + }) + + describe('when the oracle does not provide enough gas', () => { + // if updating this defaultGasLimit, be sure it matches with the + // defaultGasLimit specified in store/tx_manager.go + const defaultGasLimit = 500000 + + beforeEach(async () => { + matchers.bigNum(0, await oc.withdrawable()) + }) + + it('does not allow the oracle to withdraw the payment', async () => { + await matchers.evmRevert(async () => { + await oc.connect(roles.oracleNode).fulfillOracleRequest( + ...oracle.convertFufillParams(request, response, { + gasLimit: 70000, + }), + ) + }) + + matchers.bigNum(0, await oc.withdrawable()) + }) + + it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { + await oc.connect(roles.oracleNode).fulfillOracleRequest( + ...oracle.convertFufillParams(request, response, { + gasLimit: defaultGasLimit, + }), + ) + + matchers.bigNum(request.payment, await oc.withdrawable()) + }) + }) + }) + + describe('with a malicious requester', () => { + beforeEach(async () => { + const paymentAmount = h.toWei('1') + maliciousRequester = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address) + await link.transfer(maliciousRequester.address, paymentAmount) + }) + + it('cannot cancel before the expiration', async () => { + await matchers.evmRevert(async () => { + await maliciousRequester.maliciousRequestCancel( + specId, + ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), + ) + }) + }) + + it('cannot call functions on the LINK token through callbacks', async () => { + await matchers.evmRevert(async () => { + await maliciousRequester.request( + specId, + link.address, + ethers.utils.toUtf8Bytes('transfer(address,uint256)'), + ) + }) + }) + + describe('requester lies about amount of LINK sent', () => { + it('the oracle uses the amount of LINK actually paid', async () => { + const tx = await maliciousRequester.maliciousPrice(specId) + const receipt = await tx.wait() + const req = oracle.decodeRunRequest(receipt.logs?.[3]) + + assert(h.toWei('1').eq(req.payment)) + }) + }) + }) + + describe('with a malicious consumer', () => { + const paymentAmount = h.toWei('1') + + beforeEach(async () => { + maliciousConsumer = await maliciousConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address) + await link.transfer(maliciousConsumer.address, paymentAmount) + }) + + describe('fails during fulfillment', () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes('assertFail(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + }) + + it('allows the oracle node to receive their payment', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const balance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(balance, 0) + + await oc + .connect(roles.defaultAccount) + .withdraw(roles.oracleNode.address, paymentAmount) + + const newBalance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(paymentAmount, newBalance) + }) + + it("can't fulfill the data again", async () => { + const response2 = 'hack the planet 102' + + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + await matchers.evmRevert(async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response2), + ) + }) + }) + }) + + describe('calls selfdestruct', () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + await maliciousConsumer.remove() + }) + + it('allows the oracle node to receive their payment', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const balance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(balance, 0) + + await oc + .connect(roles.defaultAccount) + .withdraw(roles.oracleNode.address, paymentAmount) + const newBalance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(paymentAmount, newBalance) + }) + }) + + describe('request is canceled during fulfillment', () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes('cancelRequestOnFulfill(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + + matchers.bigNum(0, await link.balanceOf(maliciousConsumer.address)) + }) + + it('allows the oracle node to receive their payment', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const mockBalance = await link.balanceOf(maliciousConsumer.address) + matchers.bigNum(mockBalance, 0) + + const balance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(balance, 0) + + await oc + .connect(roles.defaultAccount) + .withdraw(roles.oracleNode.address, paymentAmount) + const newBalance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(paymentAmount, newBalance) + }) + + it("can't fulfill the data again", async () => { + const response2 = 'hack the planet 102' + + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + await matchers.evmRevert(async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response2), + ) + }) + }) + }) + + describe('tries to steal funds from node', () => { + it('is not successful with call', async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes('stealEthCall(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + matchers.bigNum( + 0, + await provider.getBalance(maliciousConsumer.address), + ) + }) + + it('is not successful with send', async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes('stealEthSend(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + matchers.bigNum( + 0, + await provider.getBalance(maliciousConsumer.address), + ) + }) + + it('is not successful with transfer', async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes('stealEthTransfer(bytes32,bytes32)'), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + matchers.bigNum( + 0, + await provider.getBalance(maliciousConsumer.address), + ) + }) + }) + }) + }) + + describe('#withdraw', () => { + describe('without reserving funds via oracleRequest', () => { + it('does nothing', async () => { + let balance = await link.balanceOf(roles.oracleNode.address) + assert.equal(0, balance.toNumber()) + await matchers.evmRevert(async () => { + await oc + .connect(roles.defaultAccount) + .withdraw(roles.oracleNode.address, h.toWei('1')) + }) + balance = await link.balanceOf(roles.oracleNode.address) + assert.equal(0, balance.toNumber()) + }) + }) + + describe('reserving funds via oracleRequest', () => { + const payment = 15 + let request: ReturnType + + beforeEach(async () => { + const mock = await getterSetterFactory + .connect(roles.defaultAccount) + .deploy() + const args = oracle.encodeOracleRequest( + specId, + mock.address, + fHash, + 0, + '0x0', + ) + const tx = await link.transferAndCall(oc.address, payment, args) + const receipt = await tx.wait() + assert.equal(3, receipt.logs?.length) + request = oracle.decodeRunRequest(receipt.logs?.[2]) + }) + + describe('but not freeing funds w fulfillOracleRequest', () => { + it('does not transfer funds', async () => { + await matchers.evmRevert(async () => { + await oc + .connect(roles.defaultAccount) + .withdraw(roles.oracleNode.address, payment) + }) + const balance = await link.balanceOf(roles.oracleNode.address) + assert.equal(0, balance.toNumber()) + }) + }) + + describe('and freeing funds', () => { + beforeEach(async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, 'Hello World!'), + ) + }) + + it('does not allow input greater than the balance', async () => { + const originalOracleBalance = await link.balanceOf(oc.address) + const originalStrangerBalance = await link.balanceOf( + roles.stranger.address, + ) + const withdrawalAmount = payment + 1 + + assert.isAbove(withdrawalAmount, originalOracleBalance.toNumber()) + await matchers.evmRevert(async () => { + await oc + .connect(roles.defaultAccount) + .withdraw(roles.stranger.address, withdrawalAmount) + }) + + const newOracleBalance = await link.balanceOf(oc.address) + const newStrangerBalance = await link.balanceOf( + roles.stranger.address, + ) + + assert.equal( + originalOracleBalance.toNumber(), + newOracleBalance.toNumber(), + ) + assert.equal( + originalStrangerBalance.toNumber(), + newStrangerBalance.toNumber(), + ) + }) + + it('allows transfer of partial balance by owner to specified address', async () => { + const partialAmount = 6 + const difference = payment - partialAmount + await oc + .connect(roles.defaultAccount) + .withdraw(roles.stranger.address, partialAmount) + const strangerBalance = await link.balanceOf(roles.stranger.address) + const oracleBalance = await link.balanceOf(oc.address) + assert.equal(partialAmount, strangerBalance.toNumber()) + assert.equal(difference, oracleBalance.toNumber()) + }) + + it('allows transfer of entire balance by owner to specified address', async () => { + await oc + .connect(roles.defaultAccount) + .withdraw(roles.stranger.address, payment) + const balance = await link.balanceOf(roles.stranger.address) + assert.equal(payment, balance.toNumber()) + }) + + it('does not allow a transfer of funds by non-owner', async () => { + await matchers.evmRevert(async () => { + await oc + .connect(roles.stranger) + .withdraw(roles.stranger.address, payment) + }) + const balance = await link.balanceOf(roles.stranger.address) + assert.isTrue(ethers.constants.Zero.eq(balance)) + }) + }) + }) + }) + + describe('#withdrawable', () => { + let request: ReturnType + + beforeEach(async () => { + const amount = h.toWei('1') + const mock = await getterSetterFactory + .connect(roles.defaultAccount) + .deploy() + const args = oracle.encodeOracleRequest( + specId, + mock.address, + fHash, + 0, + '0x0', + ) + const tx = await link.transferAndCall(oc.address, amount, args) + const receipt = await tx.wait() + assert.equal(3, receipt.logs?.length) + request = oracle.decodeRunRequest(receipt.logs?.[2]) + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(request, 'Hello World!'), + ) + }) + + it('returns the correct value', async () => { + const withdrawAmount = await oc.withdrawable() + matchers.bigNum(withdrawAmount, request.payment) + }) + }) + + describe('#cancelOracleRequest', () => { + describe('with no pending requests', () => { + it('fails', async () => { + const fakeRequest: oracle.RunRequest = { + requestId: ethers.utils.formatBytes32String('1337'), + payment: '0', + callbackFunc: + getterSetterFactory.interface.functions.requestedBytes32.sighash, + expiration: '999999999999', + + callbackAddr: '', + data: Buffer.from(''), + dataVersion: 0, + specId: '', + requester: '', + topic: '', + } + await h.increaseTime5Minutes(provider) + + await matchers.evmRevert(async () => { + await oc + .connect(roles.stranger) + .cancelOracleRequest(...oracle.convertCancelParams(fakeRequest)) + }) + }) + }) + + describe('with a pending request', () => { + const startingBalance = 100 + let request: ReturnType + let receipt: ethers.providers.TransactionReceipt + + beforeEach(async () => { + const requestAmount = 20 + + await link.transfer(roles.consumer.address, startingBalance) + + const args = oracle.encodeOracleRequest( + specId, + roles.consumer.address, + fHash, + 1, + '0x0', + ) + const tx = await link + .connect(roles.consumer) + .transferAndCall(oc.address, requestAmount, args) + receipt = await tx.wait() + + assert.equal(3, receipt.logs?.length) + request = oracle.decodeRunRequest(receipt.logs?.[2]) + }) + + it('has correct initial balances', async () => { + const oracleBalance = await link.balanceOf(oc.address) + matchers.bigNum(request.payment, oracleBalance) + + const consumerAmount = await link.balanceOf(roles.consumer.address) + assert.equal( + startingBalance - Number(request.payment), + consumerAmount.toNumber(), + ) + }) + + describe('from a stranger', () => { + it('fails', async () => { + await matchers.evmRevert(async () => { + await oc + .connect(roles.consumer) + .cancelOracleRequest(...oracle.convertCancelParams(request)) + }) + }) + }) + + describe('from the requester', () => { + it('refunds the correct amount', async () => { + await h.increaseTime5Minutes(provider) + await oc + .connect(roles.consumer) + .cancelOracleRequest(...oracle.convertCancelParams(request)) + const balance = await link.balanceOf(roles.consumer.address) + + assert.equal(startingBalance, balance.toNumber()) // 100 + }) + + it('triggers a cancellation event', async () => { + await h.increaseTime5Minutes(provider) + const tx = await oc + .connect(roles.consumer) + .cancelOracleRequest(...oracle.convertCancelParams(request)) + const receipt = await tx.wait() + + assert.equal(receipt.logs?.length, 2) + assert.equal(request.requestId, receipt.logs?.[0].topics[1]) + }) + + it('fails when called twice', async () => { + await h.increaseTime5Minutes(provider) + await oc + .connect(roles.consumer) + .cancelOracleRequest(...oracle.convertCancelParams(request)) + + await matchers.evmRevert( + oc + .connect(roles.consumer) + .cancelOracleRequest(...oracle.convertCancelParams(request)), + ) + }) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/Pointer.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Pointer.test.ts new file mode 100644 index 0000000..34d7478 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/Pointer.test.ts @@ -0,0 +1,40 @@ +import { contract, matchers, setup } from '@chainlink/test-helpers' +import { assert } from 'chai' +import { Pointer__factory } from '../../ethers/v0.4/factories/Pointer__factory' + +const pointerFactory = new Pointer__factory() +const linkTokenFactory = new contract.LinkToken__factory() +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('Pointer', () => { + let pointer: contract.Instance + let link: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + pointer = await pointerFactory + .connect(roles.defaultAccount) + .deploy(link.address) + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(pointer, ['getAddress']) + }) + + describe('#getAddress', () => { + it('returns the LINK token address', async () => { + assert.equal(await pointer.getAddress(), link.address) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.4/UpdatableConsumer.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.4/UpdatableConsumer.test.ts new file mode 100644 index 0000000..767a598 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.4/UpdatableConsumer.test.ts @@ -0,0 +1,224 @@ +import { + contract, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { ENSRegistry__factory } from '../../ethers/v0.4/factories/ENSRegistry__factory' +import { Oracle__factory } from '../../ethers/v0.4/factories/Oracle__factory' +import { PublicResolver__factory } from '../../ethers/v0.4/factories/PublicResolver__factory' +import { UpdatableConsumer__factory } from '../../ethers/v0.4/factories/UpdatableConsumer__factory' + +const linkTokenFactory = new contract.LinkToken__factory() +const ensRegistryFactory = new ENSRegistry__factory() +const oracleFactory = new Oracle__factory() +const publicResolverFacotory = new PublicResolver__factory() +const updatableConsumerFactory = new UpdatableConsumer__factory() + +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('UpdatableConsumer', () => { + // https://github.com/ethers-io/ethers-ens/blob/master/src.ts/index.ts#L631 + const ensRoot = ethers.utils.namehash('') + const tld = 'cltest' + const tldSubnode = ethers.utils.namehash(tld) + const domain = 'chainlink' + const domainNode = ethers.utils.namehash(`${domain}.${tld}`) + const tokenSubdomain = 'link' + const tokenSubnode = ethers.utils.namehash( + `${tokenSubdomain}.${domain}.${tld}`, + ) + const oracleSubdomain = 'oracle' + const oracleSubnode = ethers.utils.namehash( + `${oracleSubdomain}.${domain}.${tld}`, + ) + const specId = ethers.utils.formatBytes32String('someSpecID') + const newOracleAddress = '0xf000000000000000000000000000000000000ba7' + + let ens: contract.Instance + let ensResolver: contract.Instance + let link: contract.Instance + let oc: contract.Instance + let uc: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + oc = await oracleFactory.connect(roles.oracleNode).deploy(link.address) + ens = await ensRegistryFactory.connect(roles.defaultAccount).deploy() + + ensResolver = await publicResolverFacotory + .connect(roles.defaultAccount) + .deploy(ens.address) + const ensOracleNode = ens.connect(roles.oracleNode) + const ensResolverOracleNode = ensResolver.connect(roles.oracleNode) + + // register tld + await ens.setSubnodeOwner( + ensRoot, + h.keccak(ethers.utils.toUtf8Bytes(tld)), + roles.defaultAccount.address, + ) + + // register domain + await ens.setSubnodeOwner( + tldSubnode, + h.keccak(ethers.utils.toUtf8Bytes(domain)), + roles.oracleNode.address, + ) + + await ensOracleNode.setResolver(domainNode, ensResolver.address) + + // register token subdomain to point to token contract + await ensOracleNode.setSubnodeOwner( + domainNode, + h.keccak(ethers.utils.toUtf8Bytes(tokenSubdomain)), + roles.oracleNode.address, + ) + await ensOracleNode.setResolver(tokenSubnode, ensResolver.address) + await ensResolverOracleNode.setAddr(tokenSubnode, link.address) + + // register oracle subdomain to point to oracle contract + await ensOracleNode.setSubnodeOwner( + domainNode, + h.keccak(ethers.utils.toUtf8Bytes(oracleSubdomain)), + roles.oracleNode.address, + ) + await ensOracleNode.setResolver(oracleSubnode, ensResolver.address) + await ensResolverOracleNode.setAddr(oracleSubnode, oc.address) + + // deploy updatable consumer contract + uc = await updatableConsumerFactory + .connect(roles.defaultAccount) + .deploy(specId, ens.address, domainNode) + }) + + beforeEach(async () => { + await deployment() + }) + + describe('constructor', () => { + it('pulls the token contract address from the resolver', async () => { + assert.equal(link.address, await uc.getChainlinkToken()) + }) + + it('pulls the oracle contract address from the resolver', async () => { + assert.equal(oc.address, await uc.getOracle()) + }) + }) + + describe('#updateOracle', () => { + describe('when the ENS resolver has been updated', () => { + beforeEach(async () => { + await ensResolver + .connect(roles.oracleNode) + .setAddr(oracleSubnode, newOracleAddress) + }) + + it("updates the contract's oracle address", async () => { + await uc.updateOracle() + assert.equal( + newOracleAddress.toLowerCase(), + (await uc.getOracle()).toLowerCase(), + ) + }) + }) + + describe('when the ENS resolver has not been updated', () => { + it('keeps the same oracle address', async () => { + await uc.updateOracle() + + assert.equal(oc.address, await uc.getOracle()) + }) + }) + }) + + describe('#fulfillOracleRequest', () => { + const response = ethers.utils.formatBytes32String('1,000,000.00') + const currency = 'USD' + const paymentAmount = h.toWei('1') + let request: oracle.RunRequest + + beforeEach(async () => { + await link.transfer(uc.address, paymentAmount) + const tx = await uc.requestEthereumPrice( + h.toHex(ethers.utils.toUtf8Bytes(currency)), + ) + const receipt = await tx.wait() + request = oracle.decodeRunRequest(receipt.logs?.[3]) + }) + + it('records the data given to it by the oracle', async () => { + await oc.fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const currentPrice = await uc.currentPrice() + assert.equal(currentPrice, response) + }) + + describe('when the oracle address is updated before a request is fulfilled', () => { + beforeEach(async () => { + await ensResolver + .connect(roles.oracleNode) + .setAddr(oracleSubnode, newOracleAddress) + await uc.updateOracle() + assert.equal( + newOracleAddress.toLowerCase(), + (await uc.getOracle()).toLowerCase(), + ) + }) + + it('records the data given to it by the old oracle contract', async () => { + await oc.fulfillOracleRequest( + ...oracle.convertFufillParams(request, response), + ) + + const currentPrice = await uc.currentPrice() + assert.equal(currentPrice, response) + }) + + it('does not accept responses from the new oracle for the old requests', async () => { + await matchers.evmRevert(async () => { + await uc + .connect(roles.oracleNode) + .fulfill(request.requestId, h.toHex(response)) + }) + + const currentPrice = await uc.currentPrice() + assert.equal(ethers.utils.parseBytes32String(currentPrice), '') + }) + + it('still allows funds to be withdrawn from the oracle', async () => { + await h.increaseTime5Minutes(provider) + matchers.bigNum( + 0, + await link.balanceOf(uc.address), + 'Initial balance should be 0', + ) + + await uc.cancelRequest( + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ) + + matchers.bigNum( + paymentAmount, + await link.balanceOf(uc.address), + 'Oracle should have been repaid on cancellation.', + ) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/BasicConsumer.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/BasicConsumer.test.ts new file mode 100644 index 0000000..419b9f3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/BasicConsumer.test.ts @@ -0,0 +1,241 @@ +import { + contract, + debug, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import cbor from 'cbor' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { BasicConsumer__factory } from '../../ethers/v0.5/factories/BasicConsumer__factory' +import { Oracle__factory } from '../../ethers/v0.5/factories/Oracle__factory' + +const d = debug.makeDebug('BasicConsumer') +const basicConsumerFactory = new BasicConsumer__factory() +const oracleFactory = new Oracle__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +// create ethers provider from that web3js instance +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('BasicConsumer', () => { + const specId = '0x4c7b7ffb66b344fbaa64995af81e355a'.padEnd(66, '0') + const currency = 'USD' + const payment = h.toWei('1') + let link: contract.Instance + let oc: contract.Instance + let cc: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + oc = await oracleFactory.connect(roles.oracleNode).deploy(link.address) + cc = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, oc.address, specId) + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a predictable gas price', async () => { + const rec = await provider.getTransactionReceipt( + cc.deployTransaction.hash ?? '', + ) + assert.isBelow(rec.gasUsed?.toNumber() ?? -1, 1750000) + }) + + describe('#requestEthereumPrice', () => { + describe('without LINK', () => { + it('reverts', () => + matchers.evmRevert(cc.requestEthereumPrice(currency, payment))) + }) + + describe('with LINK', () => { + beforeEach(async () => { + await link.transfer(cc.address, h.toWei('1')) + }) + + it('triggers a log event in the Oracle contract', async () => { + const tx = await cc.requestEthereumPrice(currency, payment) + const receipt = await tx.wait() + + const log = receipt?.logs?.[3] + assert.equal(log?.address.toLowerCase(), oc.address.toLowerCase()) + + const request = oracle.decodeRunRequest(log) + const expected = { + path: ['USD'], + get: + 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY', + } + + assert.equal(h.toHex(specId), request.specId) + matchers.bigNum(h.toWei('1'), request.payment) + assert.equal(cc.address.toLowerCase(), request.requester.toLowerCase()) + assert.equal(1, request.dataVersion) + assert.deepEqual(expected, cbor.decodeFirstSync(request.data)) + }) + + it('has a reasonable gas cost', async () => { + const tx = await cc.requestEthereumPrice(currency, payment) + const receipt = await tx.wait() + + assert.isBelow(receipt?.gasUsed?.toNumber() ?? -1, 130000) + }) + }) + }) + + describe('#fulfillOracleRequest', () => { + const response = ethers.utils.formatBytes32String('1,000,000.00') + let request: oracle.RunRequest + + beforeEach(async () => { + await link.transfer(cc.address, h.toWei('1')) + const tx = await cc.requestEthereumPrice(currency, payment) + const receipt = await tx.wait() + + request = oracle.decodeRunRequest(receipt?.logs?.[3]) + }) + + it('records the data given to it by the oracle', async () => { + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest(...oracle.convertFufillParams(request, response)) + + const currentPrice = await cc.currentPrice() + assert.equal(currentPrice, response) + }) + + it('logs the data given to it by the oracle', async () => { + const tx = await oc + .connect(roles.oracleNode) + .fulfillOracleRequest(...oracle.convertFufillParams(request, response)) + const receipt = await tx.wait() + + assert.equal(2, receipt?.logs?.length) + const log = receipt?.logs?.[1] + + assert.equal(log?.topics[2], response) + }) + + describe('when the consumer does not recognize the request ID', () => { + let otherRequest: oracle.RunRequest + + beforeEach(async () => { + // Create a request directly via the oracle, rather than through the + // chainlink client (consumer). The client should not respond to + // fulfillment of this request, even though the oracle will faithfully + // forward the fulfillment to it. + const args = oracle.encodeOracleRequest( + h.toHex(specId), + cc.address, + basicConsumerFactory.interface.functions.fulfill.sighash, + 43, + '0x0', + ) + const tx = await link.transferAndCall(oc.address, 0, args) + const receipt = await tx.wait() + + otherRequest = oracle.decodeRunRequest(receipt?.logs?.[2]) + }) + + it('does not accept the data provided', async () => { + d('otherRequest %s', otherRequest) + await oc + .connect(roles.oracleNode) + .fulfillOracleRequest( + ...oracle.convertFufillParams(otherRequest, response), + ) + + const received = await cc.currentPrice() + + assert.equal(ethers.utils.parseBytes32String(received), '') + }) + }) + + describe('when called by anyone other than the oracle contract', () => { + it('does not accept the data provided', async () => { + await matchers.evmRevert( + cc.connect(roles.oracleNode).fulfill(request.requestId, response), + ) + + const received = await cc.currentPrice() + assert.equal(ethers.utils.parseBytes32String(received), '') + }) + }) + }) + + describe('#cancelRequest', () => { + const depositAmount = h.toWei('1') + let request: oracle.RunRequest + + beforeEach(async () => { + await link.transfer(cc.address, depositAmount) + const tx = await cc.requestEthereumPrice(currency, payment) + const receipt = await tx.wait() + + request = oracle.decodeRunRequest(receipt.logs?.[3]) + }) + + describe('before 5 minutes', () => { + it('cant cancel the request', () => + matchers.evmRevert( + cc + .connect(roles.consumer) + .cancelRequest( + oc.address, + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ), + )) + }) + + describe('after 5 minutes', () => { + it('can cancel the request', async () => { + await h.increaseTime5Minutes(provider) + + await cc + .connect(roles.consumer) + .cancelRequest( + oc.address, + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ) + }) + }) + }) + + describe('#withdrawLink', () => { + const depositAmount = h.toWei('1') + + beforeEach(async () => { + await link.transfer(cc.address, depositAmount) + const balance = await link.balanceOf(cc.address) + matchers.bigNum(balance, depositAmount) + }) + + it('transfers LINK out of the contract', async () => { + await cc.connect(roles.consumer).withdrawLink() + const ccBalance = await link.balanceOf(cc.address) + const consumerBalance = ethers.utils.bigNumberify( + await link.balanceOf(roles.consumer.address), + ) + matchers.bigNum(ccBalance, 0) + matchers.bigNum(consumerBalance, depositAmount) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/BasicServiceAgreementConsumer.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/BasicServiceAgreementConsumer.test.ts new file mode 100644 index 0000000..f6af228 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/BasicServiceAgreementConsumer.test.ts @@ -0,0 +1,182 @@ +import { + contract, + coordinator, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import cbor from 'cbor' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { Coordinator__factory } from '../../ethers/v0.5/factories/Coordinator__factory' +import { MeanAggregator__factory } from '../../ethers/v0.5/factories/MeanAggregator__factory' +import { ServiceAgreementConsumer__factory } from '../../ethers/v0.5/factories/ServiceAgreementConsumer__factory' + +const coordinatorFactory = new Coordinator__factory() +const meanAggregatorFactory = new MeanAggregator__factory() +const serviceAgreementConsumerFactory = new ServiceAgreementConsumer__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +// create ethers provider from that web3js instance +const provider = setup.provider() + +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('ServiceAgreementConsumer', () => { + const currency = 'USD' + + let link: contract.Instance + let coord: contract.Instance + let cc: contract.Instance + let agreement: coordinator.ServiceAgreement + + beforeEach(async () => { + const meanAggregator = await meanAggregatorFactory + .connect(roles.defaultAccount) + .deploy() + agreement = await coordinator.serviceAgreement({ + aggregator: meanAggregator.address, + oracles: [roles.oracleNode], + }) + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + coord = await coordinatorFactory + .connect(roles.defaultAccount) + .deploy(link.address) + await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(agreement)), + ) + cc = await serviceAgreementConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, coord.address, coordinator.generateSAID(agreement)) + }) + + it('gas price of contract deployment is predictable', async () => { + const rec = await provider.getTransactionReceipt( + cc.deployTransaction.hash ?? '', + ) + assert.isBelow(rec.gasUsed?.toNumber() ?? 0, 1500000) + }) + + describe('#requestEthereumPrice', () => { + describe('without LINK', () => { + it('reverts', async () => { + await matchers.evmRevert(async () => { + await cc.requestEthereumPrice(currency) + }) + }) + }) + + describe('with LINK', () => { + const paymentAmount = h.toWei('1') + beforeEach(async () => { + await link.transfer(cc.address, paymentAmount) + }) + + it('triggers a log event in the Coordinator contract', async () => { + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + const log = receipt?.logs?.[3] + assert.equal(log?.address.toLowerCase(), coord.address.toLowerCase()) + + const request = oracle.decodeRunRequest(log) + + assert.equal(coordinator.generateSAID(agreement), request.specId) + matchers.bigNum(paymentAmount, request.payment) + assert.equal(cc.address.toLowerCase(), request.requester.toLowerCase()) + assert.equal(1, request.dataVersion) + + const expected = { + path: currency, + get: + 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY', + } + assert.deepEqual(expected, cbor.decodeFirstSync(request.data)) + }) + + it('has a reasonable gas cost', async () => { + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + + assert.isBelow(receipt?.gasUsed?.toNumber() ?? -1, 175000) + }) + }) + + describe('#fulfillOracleRequest', () => { + const response = ethers.utils.formatBytes32String('1,000,000.00') + let request: oracle.RunRequest + + beforeEach(async () => { + await link.transfer(cc.address, h.toWei('1')) + const tx = await cc.requestEthereumPrice(currency) + const receipt = await tx.wait() + const log = receipt?.logs?.[3] + assert.equal(log?.address.toLowerCase(), coord.address.toLowerCase()) + + request = oracle.decodeRunRequest(log) + }) + + it('records the data given to it by the oracle', async () => { + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest(request.requestId, response) + const currentPrice = await cc.currentPrice() + assert.equal(currentPrice, response) + }) + + describe('when the consumer does not recognize the request ID', () => { + let request2: oracle.RunRequest + + beforeEach(async () => { + // Create a request directly via the oracle, rather than through the + // chainlink client (consumer). The client should not respond to + // fulfillment of this request, even though the oracle will faithfully + // forward the fulfillment to it. + const args = oracle.encodeOracleRequest( + coordinator.generateSAID(agreement), + cc.address, + serviceAgreementConsumerFactory.interface.functions.fulfill.sighash, + 48, + '0x0', + ) + + const tx = await link.transferAndCall( + coord.address, + agreement.payment, + args, + ) + const receipt = await tx.wait() + + request2 = oracle.decodeRunRequest(receipt?.logs?.[2]) + }) + + it('does not accept the data provided', async () => { + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest(request2.requestId, response) + + const received = await cc.currentPrice() + assert.equal(ethers.utils.parseBytes32String(received), '') + }) + }) + + describe('when called by anyone other than the oracle contract', () => { + it('does not accept the data provided', async () => { + await matchers.evmRevert(async () => { + await cc + .connect(roles.oracleNode) + .fulfill(request.requestId, response) + }) + const received = await cc.currentPrice() + assert.equal(ethers.utils.parseBytes32String(received), '') + }) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/Chainlink.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Chainlink.test.ts new file mode 100644 index 0000000..42552f8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Chainlink.test.ts @@ -0,0 +1,201 @@ +import { + contract, + helpers as h, + matchers, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { ContractReceipt } from 'ethers/contract' +import { ChainlinkTestHelper__factory } from '../../ethers/v0.5/factories/ChainlinkTestHelper__factory' + +const chainlinkFactory = new ChainlinkTestHelper__factory() +const provider = setup.provider() + +let defaultAccount: ethers.Wallet +beforeAll(async () => { + defaultAccount = await setup + .users(provider) + .then((x) => x.roles.defaultAccount) +}) + +describe('Chainlink', () => { + let cl: contract.Instance + let clEvents: contract.Instance< + ChainlinkTestHelper__factory + >['interface']['events'] + + const deployment = setup.snapshot(provider, async () => { + cl = await chainlinkFactory.connect(defaultAccount).deploy() + clEvents = cl.interface.events + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(chainlinkFactory, [ + 'add', + 'addBytes', + 'addInt', + 'addStringArray', + 'addUint', + 'closeEvent', + 'setBuffer', + ]) + }) + + function getPayloadFrom(receipt: ContractReceipt) { + const { payload } = h.eventArgs( + h.findEventIn(receipt, clEvents.RequestData), + ) + const decoded = h.decodeDietCBOR(payload) + return decoded + } + + describe('#close', () => { + it('handles empty payloads', async () => { + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, {}) + }) + }) + + describe('#setBuffer', () => { + it('emits the buffer', async () => { + await cl.setBuffer('0xA161616162') + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { a: 'b' }) + }) + }) + + describe('#add', () => { + it('stores and logs keys and values', async () => { + await cl.add('first', 'word!!') + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { first: 'word!!' }) + }) + + it('handles two entries', async () => { + await cl.add('first', 'uno') + await cl.add('second', 'dos') + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { + first: 'uno', + second: 'dos', + }) + }) + }) + + describe('#addBytes', () => { + it('stores and logs keys and values', async () => { + await cl.addBytes('first', '0xaabbccddeeff') + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + const expected = h.hexToBuf('0xaabbccddeeff') + assert.deepEqual(decoded, { first: expected }) + }) + + it('handles two entries', async () => { + await cl.addBytes('first', '0x756E6F') + await cl.addBytes('second', '0x646F73') + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + const expectedFirst = h.hexToBuf('0x756E6F') + const expectedSecond = h.hexToBuf('0x646F73') + assert.deepEqual(decoded, { + first: expectedFirst, + second: expectedSecond, + }) + }) + + it('handles strings', async () => { + await cl.addBytes('first', ethers.utils.toUtf8Bytes('apple')) + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + const expected = ethers.utils.toUtf8Bytes('apple') + assert.deepEqual(decoded, { first: expected }) + }) + }) + + describe('#addInt', () => { + it('stores and logs keys and values', async () => { + await cl.addInt('first', 1) + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { first: 1 }) + }) + + it('handles two entries', async () => { + await cl.addInt('first', 1) + await cl.addInt('second', 2) + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { + first: 1, + second: 2, + }) + }) + }) + + describe('#addUint', () => { + it('stores and logs keys and values', async () => { + await cl.addUint('first', 1) + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { first: 1 }) + }) + + it('handles two entries', async () => { + await cl.addUint('first', 1) + await cl.addUint('second', 2) + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { + first: 1, + second: 2, + }) + }) + }) + + describe('#addStringArray', () => { + it('stores and logs keys and values', async () => { + await cl.addStringArray('word', [ + ethers.utils.formatBytes32String('seinfeld'), + ethers.utils.formatBytes32String('"4"'), + ethers.utils.formatBytes32String('LIFE'), + ]) + const tx = await cl.closeEvent() + const receipt = await tx.wait() + const decoded = getPayloadFrom(receipt) + + assert.deepEqual(decoded, { word: ['seinfeld', '"4"', 'LIFE'] }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/Coordinator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Coordinator.test.ts new file mode 100644 index 0000000..8bb1048 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Coordinator.test.ts @@ -0,0 +1,979 @@ +import { + contract, + coordinator, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { ContractReceipt } from 'ethers/contract' +import { BigNumberish } from 'ethers/utils' +import { Coordinator__factory } from '../../ethers/v0.5/factories/Coordinator__factory' +import { EmptyAggregator__factory } from '../../ethers/v0.5/factories/EmptyAggregator__factory' +import { GetterSetter__factory } from '../../ethers/v0.5/factories/GetterSetter__factory' +import { MaliciousConsumer__factory } from '../../ethers/v0.5/factories/MaliciousConsumer__factory' +import { MaliciousRequester__factory } from '../../ethers/v0.5/factories/MaliciousRequester__factory' +import { MeanAggregator__factory } from '../../ethers/v0.5/factories/MeanAggregator__factory' + +const provider = setup.provider() + +const linkTokenFactory = new contract.LinkToken__factory() +const coordinatorFactory = new Coordinator__factory() +const emptyAggregatorFactory = new EmptyAggregator__factory() +const meanAggregatorFactory = new MeanAggregator__factory() +const getterSetterFactory = new GetterSetter__factory() +const maliciousRequesterFactory = new MaliciousRequester__factory() +const maliciousConsumerFactory = new MaliciousConsumer__factory() + +const oracleRequestEvent = coordinatorFactory.interface.events.OracleRequest +const newServiceAgreementEvent = + coordinatorFactory.interface.events.NewServiceAgreement + +let roles: setup.Roles + +let link: contract.Instance +let coord: contract.Instance +let emptyAggregator: contract.Instance +let meanAggregator: contract.Instance +let oracle1: string +let oracle2: string +let oracle3: string + +const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + coord = await coordinatorFactory + .connect(roles.defaultAccount) + .deploy(link.address) + emptyAggregator = await emptyAggregatorFactory + .connect(roles.defaultAccount) + .deploy() + meanAggregator = await meanAggregatorFactory + .connect(roles.defaultAccount) + .deploy() +}) + +beforeAll(async () => { + roles = await setup.users(provider).then((x) => x.roles) + oracle1 = roles.oracleNode1.address + oracle2 = roles.oracleNode2.address + oracle3 = roles.oracleNode3.address +}) + +beforeEach(deployment) + +describe('Coordinator', () => { + it('has a limited public interface', () => { + matchers.publicAbi(coordinatorFactory, [ + 'EXPIRY_TIME', + 'balanceOf', + 'cancelOracleRequest', + 'depositFunds', + 'fulfillOracleRequest', + 'getId', + 'initiateServiceAgreement', + 'onTokenTransfer', + 'oracleRequest', + 'serviceAgreements', + 'withdraw', + 'withdrawableTokens', + ]) + }) + + describe('#getId', () => { + it('matches the ID generated by the oracle off-chain', async () => { + const agreement = coordinator.serviceAgreement({ + payment: 1, + expiration: 2, + requestDigest: + '0x85820c5ec619a1f517ee6cfeff545ec0ca1a90206e1a38c47f016d4137e801dd', + aggregator: emptyAggregator.address, + }) + const sAID = coordinator.generateSAID(agreement) + const sAAsData = coordinator.encodeServiceAgreement(agreement) + const result = await coord.getId(sAAsData) + assert.equal(result.toLowerCase(), sAID) + }) + }) + + describe('#initiateServiceAgreement', () => { + describe('with valid oracle signatures', () => { + let serviceAgreement: coordinator.ServiceAgreement + let sAID: string + let receipt: ContractReceipt + + beforeEach(async () => { + serviceAgreement = coordinator.serviceAgreement({ + oracles: [roles.oracleNode], + aggregator: emptyAggregator.address, + }) + sAID = coordinator.generateSAID(serviceAgreement) + const tx = await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(serviceAgreement)), + ) + receipt = await tx.wait() + }) + + it('saves a service agreement struct from the parameters', async () => { + const sAID = coordinator.generateSAID(serviceAgreement) + const sa = await coord.serviceAgreements(sAID) + + matchers.bigNum( + sa.payment, + serviceAgreement.payment, + 'expected payment', + ) + matchers.bigNum( + sa.expiration, + serviceAgreement.expiration, + 'expected expiration', + ) + matchers.bigNum(sa.endAt, serviceAgreement.endAt, 'expected endAt date') + assert.equal( + sa.requestDigest, + serviceAgreement.requestDigest, + 'expected requestDigest', + ) + }) + + it('generates the SAID', async () => { + const ethSAID = await coord.getId( + coordinator.encodeServiceAgreement(serviceAgreement), + ) + assert.equal(ethSAID, sAID) + }) + + it('logs an event', async () => { + expect(h.findEventIn(receipt, newServiceAgreementEvent)).toBeDefined() + }) + + it('calls the aggregator with the SA info', async () => { + const event = h.findEventIn(receipt, newServiceAgreementEvent) + assert(event, 'event was expected') + const { said } = h.eventArgs( + h.findEventIn(receipt, newServiceAgreementEvent), + ) + assert.equal(said, sAID) + }) + }) + + describe('with an invalid oracle signature', () => { + it('saves no service agreement struct, if signatures invalid', async () => { + const serviceAgreement = coordinator.serviceAgreement({ + oracles: [roles.oracleNode], + aggregator: emptyAggregator.address, + }) + const sAID = coordinator.generateSAID(serviceAgreement) + const badOracleSignature = await coordinator.personalSign( + sAID, + roles.stranger, + ) + const badRequestDigestAddr = coordinator.recoverAddressFromSignature( + sAID, + badOracleSignature, + ) + assert.equal(roles.stranger.address, badRequestDigestAddr) + + const conbinedSignatures = coordinator.combineOracleSignatures([ + badOracleSignature, + ]) + await matchers.evmRevert( + coord.initiateServiceAgreement( + coordinator.encodeServiceAgreement(serviceAgreement), + coordinator.encodeOracleSignatures(conbinedSignatures), + ), + ) + + const fetchedServiceAgreement = await coord.serviceAgreements(sAID) + coordinator.assertServiceAgreementEmpty(fetchedServiceAgreement) + }) + }) + + describe('Validation of service agreement deadlines', () => { + it('Rejects a service agreement with an endAt date in the past', async () => { + const serviceAgreement = coordinator.serviceAgreement({ + endAt: 1, + aggregator: emptyAggregator.address, + }) + const sAID = coordinator.generateSAID(serviceAgreement) + await matchers.evmRevert( + coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(serviceAgreement)), + ), + ) + + const fetchedServiceAgreement = await coord.serviceAgreements(sAID) + coordinator.assertServiceAgreementEmpty(fetchedServiceAgreement) + }) + }) + }) + + describe('#oracleRequest', () => { + const to = '0x80e29acb842498fe6591f020bd82766dce619d43' + let agreement: coordinator.ServiceAgreement + let fHash: string + let sAID: string + + beforeEach(async () => { + fHash = getterSetterFactory.interface.functions.requestedBytes32.sighash + agreement = coordinator.serviceAgreement({ + oracles: [roles.oracleNode], + aggregator: meanAggregator.address, + }) + sAID = coordinator.generateSAID(agreement) + await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(agreement)), + ) + await link.transfer(roles.consumer.address, h.toWei('1000')) + }) + + describe('when called through the LINK token with enough payment', () => { + it('logs an event', async () => { + const payload = coordinator.encodeOracleRequest( + sAID, + to, + fHash, + 1, + '0x0', + ) + const tx = await link + .connect(roles.consumer) + .transferAndCall(coord.address, agreement.payment, payload) + const receipt = await tx.wait() + const event = h.findEventIn(receipt, oracleRequestEvent) + const { sAId: loggedSAID } = oracleRequestEvent.decode( + event?.data ?? '', + event?.topics, + ) + const req = oracle.decodeRunRequest(event) + + assert.equal(event?.address, coord.address) + assert.equal(sAID, loggedSAID) + matchers.bigNum( + roles.consumer.address, + req.requester, + "Logged consumer address doesn't match", + ) + matchers.bigNum( + agreement.payment, + req.payment, + "Logged payment amount doesn't match", + ) + }) + }) + + describe('when called through the LINK token with not enough payment', () => { + it('throws an error', async () => { + const calldata = coordinator.encodeOracleRequest( + sAID, + to, + fHash, + 1, + '0x0', + ) + const underPaid = h + .bigNum(agreement.payment) + .sub(h.bigNum(1)) + .toString() + await matchers.evmRevert( + link + .connect(roles.consumer) + .transferAndCall(coord.address, underPaid, calldata), + ) + }) + }) + + describe('when not called through the LINK token', () => { + it('reverts', async () => { + const txPromise = coord + .connect(roles.consumer) + .oracleRequest( + ethers.constants.AddressZero, + 0, + sAID, + to, + fHash, + 1, + 1, + '0x', + ) + await matchers.evmRevert(txPromise, 'Must use LINK token') + }) + }) + }) + + describe('#fulfillOracleRequest', () => { + let agreement: coordinator.ServiceAgreement + let sAID: string + let mock: contract.Instance + let request: oracle.RunRequest + let fHash: string + beforeEach(async () => { + agreement = coordinator.serviceAgreement({ + oracles: [roles.oracleNode], + aggregator: meanAggregator.address, + }) + sAID = coordinator.generateSAID(agreement) + const tx = await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(agreement)), + ) + const receipt = await tx.wait() + const event = h.findEventIn(receipt, newServiceAgreementEvent) + const { said: loggedSAID } = newServiceAgreementEvent.decode( + event?.data ?? '', + event?.topics, + ) + assert.equal(loggedSAID, sAID) + + fHash = getterSetterFactory.interface.functions.requestedBytes32.sighash + }) + + describe('cooperative consumer', () => { + const message = h.toBytes32String('Hello World!') + beforeEach(async () => { + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() + const payload = coordinator.encodeOracleRequest( + sAID, + mock.address, + fHash, + 1, + '0x0', + ) + const tx = await link.transferAndCall( + coord.address, + agreement.payment, + payload, + ) + const receipt = await tx.wait() + const eventLog = h.findEventIn(receipt, oracleRequestEvent) + request = oracle.decodeRunRequest(eventLog) + }) + + describe('when called by a non-owner', () => { + // Turn this test on when multiple-oracle response aggregation is enabled + xit('raises an error', async () => { + await matchers.evmRevert( + coord + .connect(roles.stranger) + .fulfillOracleRequest(request.requestId, message), + ) + }) + }) + + describe('when called by an owner', () => { + it('raises an error if the request ID does not exist', async () => { + const invalidRequestId = h.toBytes32String('deadbeef') + await matchers.evmRevert( + coord + .connect(roles.oracleNode) + .fulfillOracleRequest(invalidRequestId, message), + ) + }) + + it('sets the value on the requested contract', async () => { + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest(request.requestId, message) + const mockRequestId = await mock.requestId() + assert.equal(h.toHex(request.requestId), mockRequestId) + const currentValue = await mock.getBytes32() + assert.equal('Hello World!', h.parseBytes32String(currentValue)) + }) + + it('reports errors from the aggregator, such as double-reporting', async () => { + const firstMessage = h.toBytes32String('First message!') + const seccondMessage = h.toBytes32String('Second message!!') + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest(request.requestId, firstMessage) + await matchers.evmRevert( + coord + .connect(roles.oracleNode) + .fulfillOracleRequest(request.requestId, seccondMessage), + 'oracle already reported', + ) + }) + }) + }) + + describe('with a malicious requester', () => { + let mock: contract.Instance + const paymentAmount = h.toWei('1') + + beforeEach(async () => { + mock = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, coord.address) + await link.transfer(mock.address, paymentAmount) + }) + + xit('cannot cancel before the expiration', async () => { + await matchers.evmRevert( + mock.maliciousRequestCancel(sAID, 'doesNothing(bytes32,bytes32)'), + ) + }) + + it('cannot call functions on the LINK token through callbacks', async () => { + await matchers.evmRevert( + mock.request( + sAID, + link.address, + linkTokenFactory.interface.functions.transfer.sighash, + ), + ) + }) + + describe('requester lies about amount of LINK sent', () => { + it('the oracle uses the amount of LINK actually paid', async () => { + const tx = await mock.maliciousPrice(sAID) + const receipt = await tx.wait() + const eventLog = h.findEventIn(receipt, oracleRequestEvent) + const req = oracle.decodeRunRequest(eventLog) + matchers.bigNum( + paymentAmount, + req.payment, + [ + 'Malicious data request tricked oracle into refunding more than', + 'the requester paid, by claiming a larger amount', + `(${req.payment}) than the requester paid (${paymentAmount})`, + ].join(' '), + ) + }) + }) + }) + + describe('with a malicious consumer', () => { + const paymentAmount = h.toWei('1') + let mock: contract.Instance + + beforeEach(async () => { + mock = await maliciousConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, coord.address) + await link.transfer(mock.address, paymentAmount) + }) + + describe('fails during fulfillment', () => { + beforeEach(async () => { + const tx = await mock.requestData( + sAID, + maliciousConsumerFactory.interface.functions.assertFail.sighash, + ) + const receipt = await tx.wait() + const eventLog = h.findEventIn(receipt, oracleRequestEvent) + request = oracle.decodeRunRequest(eventLog) + }) + + // needs coordinator withdrawal functionality to meet parity + xit('allows the oracle node to receive their payment', async () => { + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 101'), + ) + + const balance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(balance, 0) + + await coord + .connect(roles.oracleNode) + .withdraw(roles.oracleNode.address, paymentAmount) + const newBalance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(paymentAmount, newBalance) + }) + + it("can't fulfill the data again", async () => { + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 101'), + ) + await matchers.evmRevert( + coord + .connect(roles.oracleNode) + .fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 102'), + ), + 'oracle already reported', + ) + }) + }) + + describe('calls selfdestruct', () => { + beforeEach(async () => { + const tx = await mock.requestData( + sAID, + maliciousConsumerFactory.interface.functions.doesNothing.sighash, + ) + const receipt = await tx.wait() + const eventLog = h.findEventIn(receipt, oracleRequestEvent) + request = oracle.decodeRunRequest(eventLog) + await mock.remove() + }) + + // needs coordinator withdrawal functionality to meet parity + xit('allows the oracle node to receive their payment', async () => { + await coord.fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 101'), + ) + + const balance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(balance, 0) + + await coord.withdraw(roles.oracleNode.address, paymentAmount) + const newBalance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(paymentAmount, newBalance) + }) + }) + + describe('request is canceled during fulfillment', () => { + beforeEach(async () => { + const tx = await mock.requestData( + sAID, + maliciousConsumerFactory.interface.functions.cancelRequestOnFulfill + .sighash, + ) + const receipt = await tx.wait() + const eventLog = h.findEventIn(receipt, oracleRequestEvent) + request = oracle.decodeRunRequest(eventLog) + const mockBalance = await link.balanceOf(mock.address) + matchers.bigNum(mockBalance, h.bigNum(0)) + }) + + // needs coordinator withdrawal functionality to meet parity + xit('allows the oracle node to receive their payment', async () => { + await coord.fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 101'), + ) + const mockBalance = await link.balanceOf(mock.address) + matchers.bigNum(mockBalance, 0) + const balance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(balance, 0) + await coord.withdraw(roles.oracleNode.address, paymentAmount) + const newBalance = await link.balanceOf(roles.oracleNode.address) + matchers.bigNum(paymentAmount, newBalance) + }) + + it("can't fulfill the data again", async () => { + await coord + .connect(roles.oracleNode) + .fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 101'), + ) + await matchers.evmRevert( + coord + .connect(roles.oracleNode) + .fulfillOracleRequest( + request.requestId, + h.toBytes32String('hack the planet 102'), + ), + ) + }) + }) + }) + + describe('when aggregating answers', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + agreement = coordinator.serviceAgreement({ + aggregator: meanAggregator.address, + oracles: [roles.oracleNode1, roles.oracleNode2, roles.oracleNode3], + }) + sAID = coordinator.generateSAID(agreement) + + const tx1 = await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(agreement)), + ) + const receipt1 = await tx1.wait() + const event1 = h.findEventIn(receipt1, newServiceAgreementEvent) + const { said: loggedSAID } = newServiceAgreementEvent.decode( + event1?.data ?? '', + event1?.topics, + ) + assert.equal(loggedSAID, sAID) + + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() + + const fHash = + getterSetterFactory.interface.functions.requestedUint256.sighash + + const payload = coordinator.encodeOracleRequest( + sAID, + mock.address, + fHash, + 1, + '0x0', + ) + const tx2 = await link.transferAndCall( + coord.address, + agreement.payment, + payload, + ) + const receipt2 = await tx2.wait() + const event2 = h.findEventIn(receipt2, oracleRequestEvent) + request = oracle.decodeRunRequest(event2) + }) + + it('does not set the value with only one oracle', async () => { + const tx = await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, h.numToBytes32(17)) + const receipt = await tx.wait() + assert.equal(receipt.logs?.length, 0) // No logs emitted = consuming contract not called + }) + + it('sets the average of the reported values', async () => { + await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, h.numToBytes32(16)) + await coord + .connect(roles.oracleNode2) + .fulfillOracleRequest(request.requestId, h.numToBytes32(17)) + const tx = await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, h.numToBytes32(18)) + const receipt = await tx.wait() + assert.equal(receipt.logs?.length, 1) + const currentValue = await mock.getUint256() + matchers.bigNum(currentValue, 17) + }) + + describe('when large values are provided in response', () => { + // (uint256(-1) / 2) - 1 + const largeValue1 = + '0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe' + // (uint256(-1) / 2) + const largeValue2 = + '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + // (uint256(-1) / 2) + 1 + const largeValue3 = + '0x8000000000000000000000000000000000000000000000000000000000000000' + + beforeEach(async () => { + await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, largeValue1) + await coord + .connect(roles.oracleNode2) + .fulfillOracleRequest(request.requestId, largeValue2) + }) + + it('does not overflow', async () => { + await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, largeValue3) + }) + + it('sets the average of the reported values', async () => { + await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, largeValue3) + const currentValue = await mock.getUint256() + matchers.bigNum(largeValue2, currentValue) + assert.notEqual(h.bigNum(0), h.bigNum(await mock.requestId())) // check if called + }) + }) + + it('successfully sets average when responses equal largest uint256', async () => { + const largest = + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + + await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, largest) + await coord + .connect(roles.oracleNode2) + .fulfillOracleRequest(request.requestId, largest) + await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, largest) + const currentValue = await mock.getUint256() + matchers.bigNum(h.bigNum(largest), currentValue) + assert.notEqual(h.bigNum(0), h.bigNum(await mock.requestId())) // check if called + }) + + it('rejects oracles not part of the service agreement', async () => { + await matchers.evmRevert( + coord + .connect(roles.stranger) + .fulfillOracleRequest(request.requestId, h.numToBytes32(18)), + ) + }) + + describe('when an oracle reports multiple times', () => { + beforeEach(async () => { + await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, h.numToBytes32(16)) + await coord + .connect(roles.oracleNode2) + .fulfillOracleRequest(request.requestId, h.numToBytes32(17)) + await matchers.evmRevert( + coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, h.numToBytes32(18)), + ) + }) + + it('does not set the average', async () => { + matchers.bigNum(0, await mock.requestId()) // check if called + }) + + it('still allows the other oracles to report', async () => { + await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, h.numToBytes32(18)) + const currentValue = await mock.getUint256() + matchers.bigNum(h.bigNum(17), currentValue) + assert.notEqual(h.bigNum(0), h.bigNum(await mock.requestId())) // check if called + }) + }) + }) + + describe('after aggregation', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + agreement = coordinator.serviceAgreement({ + aggregator: meanAggregator.address, + oracles: [roles.oracleNode1, roles.oracleNode2, roles.oracleNode3], + }) + sAID = coordinator.generateSAID(agreement) + + const tx1 = await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(agreement)), + ) + const receipt1 = await tx1.wait() + const event1 = h.findEventIn(receipt1, newServiceAgreementEvent) + const { said: loggedSAID } = newServiceAgreementEvent.decode( + event1?.data ?? '', + event1?.topics, + ) + assert.equal(loggedSAID, sAID) + + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() + + const fHash = + getterSetterFactory.interface.functions.requestedUint256.sighash + + const payload = coordinator.encodeOracleRequest( + sAID, + mock.address, + fHash, + 1, + '0x0', + ) + const tx = await link.transferAndCall( + coord.address, + agreement.payment, + payload, + ) + const receipt = await tx.wait() + const eventLog = h.findEventIn(receipt, oracleRequestEvent) + request = oracle.decodeRunRequest(eventLog) + + await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, h.numToBytes32(16)) + await coord + .connect(roles.oracleNode2) + .fulfillOracleRequest(request.requestId, h.numToBytes32(17)) + await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, h.numToBytes32(18)) + + const currentValue = await mock.getUint256() + matchers.bigNum(h.bigNum(17), currentValue) + }) + + it('oracle balances are updated', async () => { + // Given the 3 oracles from the SA, each should have the following balance after fulfillment + const expected1 = h.bigNum('555555555555555555') + const expected2 = h.bigNum('333333333333333333') + const expected3 = h.bigNum('111111111111111111') + const balance1 = await coord.withdrawableTokens(oracle1) + const balance2 = await coord.withdrawableTokens(oracle2) + const balance3 = await coord.withdrawableTokens(oracle3) + matchers.bigNum(expected1, balance1) + matchers.bigNum(expected2, balance2) + matchers.bigNum(expected3, balance3) + }) + }) + + describe('withdraw', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + agreement = coordinator.serviceAgreement({ + aggregator: meanAggregator.address, + oracles: [roles.oracleNode1, roles.oracleNode2, roles.oracleNode3], + }) + sAID = coordinator.generateSAID(agreement) + + const tx1 = await coord.initiateServiceAgreement( + ...(await coordinator.initiateSAParams(agreement)), + ) + const receipt1 = await tx1.wait() + const event1 = h.findEventIn(receipt1, newServiceAgreementEvent) + const { said: loggedSAID } = newServiceAgreementEvent.decode( + event1?.data ?? '', + event1?.topics, + ) + assert.equal(loggedSAID, sAID) + + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() + + const fHash = + getterSetterFactory.interface.functions.requestedUint256.sighash + + const payload = coordinator.encodeOracleRequest( + sAID, + mock.address, + fHash, + 1, + '0x0', + ) + const tx2 = await link.transferAndCall( + coord.address, + agreement.payment, + payload, + ) + const receipt2 = await tx2.wait() + const event2 = h.findEventIn(receipt2, oracleRequestEvent) + request = oracle.decodeRunRequest(event2) + + await coord + .connect(roles.oracleNode1) + .fulfillOracleRequest(request.requestId, h.numToBytes32(16)) + await coord + .connect(roles.oracleNode2) + .fulfillOracleRequest(request.requestId, h.numToBytes32(17)) + await coord + .connect(roles.oracleNode3) + .fulfillOracleRequest(request.requestId, h.numToBytes32(18)) + + const currentValue = await mock.getUint256() + matchers.bigNum(h.bigNum(17), currentValue) + }) + + it('allows the oracle to withdraw their full amount', async () => { + const coordBalance1 = await link.balanceOf(coord.address) + const withdrawAmount = await coord.withdrawableTokens(oracle1) + await coord.connect(roles.oracleNode1).withdraw(oracle1, withdrawAmount) + const oracleBalance = await link.balanceOf(oracle1) + const afterWithdrawBalance = await coord + .connect(roles.oracleNode1) + .withdrawableTokens(oracle1) + const coordBalance2 = await link.balanceOf(coord.address) + const expectedCoordFinalBalance = coordBalance1.sub(withdrawAmount) + matchers.bigNum(withdrawAmount, oracleBalance) + matchers.bigNum(expectedCoordFinalBalance, coordBalance2) + matchers.bigNum(h.bigNum(0), afterWithdrawBalance) + }) + + it('rejects amounts greater than allowed', async () => { + const oracleBalance = await coord.withdrawableTokens(oracle1) + const withdrawAmount = oracleBalance.add(h.bigNum(1)) + await matchers.evmRevert( + coord.connect(roles.oracleNode1).withdraw(oracle1, withdrawAmount), + ) + }) + }) + }) + + describe('#depositFunds', () => { + async function assertBalances({ + link: linkBal, + coordinator: coordBal, + }: { + link: BigNumberish + coordinator: BigNumberish + }) { + const linkBalance = await link.balanceOf(oracle1) + const coordinatorBalance = await coord.balanceOf(oracle1) + matchers.bigNum(linkBalance, linkBal) + matchers.bigNum(coordinatorBalance, coordBal) + } + + beforeEach(async () => { + await link.transfer(oracle1, 4) + const initialBalance = await link.balanceOf(oracle1) + matchers.bigNum(initialBalance, 4) + }) + + it('permits deposit through link#transferAndCall', async () => { + const payload = coordinatorFactory.interface.functions.depositFunds.encode( + [oracle1, 1], + ) + await link + .connect(roles.oracleNode1) + .transferAndCall(coord.address, 1, payload) + await assertBalances({ link: 3, coordinator: 1 }) + }) + + it('overrides invalid payloads', async () => { + const payload = coordinatorFactory.interface.functions.depositFunds.encode( + [coord.address, 2], + ) + await link + .connect(roles.oracleNode1) + .transferAndCall(coord.address, 1, payload) + await assertBalances({ link: 3, coordinator: 1 }) + }) + + it('reverts with insufficient payloads', async () => { + const payload = + coordinatorFactory.interface.functions.depositFunds.sighash + await matchers.evmRevert( + link + .connect(roles.oracleNode1) + .transferAndCall(coord.address, 1, payload), + ) + }) + + it('allows partial withdrawals', async () => { + const payload = coordinatorFactory.interface.functions.depositFunds.encode( + [oracle1, 4], + ) + await link + .connect(roles.oracleNode1) + .transferAndCall(coord.address, 4, payload) + await coord.connect(roles.oracleNode1).withdraw(oracle1, 1) + await assertBalances({ link: 1, coordinator: 3 }) + }) + + it('allows full withdrawals', async () => { + const payload = coordinatorFactory.interface.functions.depositFunds.encode( + [oracle1, 4], + ) + await link + .connect(roles.oracleNode1) + .transferAndCall(coord.address, 4, payload) + await coord.connect(roles.oracleNode1).withdraw(oracle1, 2) + await coord.connect(roles.oracleNode1).withdraw(oracle1, 2) + await assertBalances({ link: 4, coordinator: 0 }) + }) + + it('reverts when overdrawing', async () => { + const payload = coordinatorFactory.interface.functions.depositFunds.encode( + [oracle1, 4], + ) + await link + .connect(roles.oracleNode1) + .transferAndCall(coord.address, 4, payload) + await coord.connect(roles.oracleNode1).withdraw(oracle1, 4) + await matchers.evmRevert( + coord.connect(roles.oracleNode1).withdraw(oracle1, 1), + ) + await assertBalances({ link: 4, coordinator: 0 }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/GetterSetter.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/GetterSetter.test.ts new file mode 100644 index 0000000..b4dc34a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/GetterSetter.test.ts @@ -0,0 +1,106 @@ +import { + contract, + helpers as h, + matchers, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { GetterSetter__factory } from '../../ethers/v0.5/factories/GetterSetter__factory' +const getterSetterFactory = new GetterSetter__factory() + +const provider = setup.provider() +let roles: setup.Roles + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('GetterSetter', () => { + const requestId = + '0x3bd198932d9cc01e2950ffc518fd38a303812200000000000000000000000000' + const bytes32 = ethers.utils.formatBytes32String('Hi Mom!') + const uint256 = ethers.utils.bigNumberify(645746535432) + + let gs: contract.Instance + const deployment = setup.snapshot(provider, async () => { + gs = await getterSetterFactory.connect(roles.defaultAccount).deploy() + }) + + beforeEach(async () => { + await deployment() + }) + + describe('#setBytes32Val', () => { + it('updates the bytes32 value', async () => { + await gs.setBytes32(bytes32) + + const currentBytes32 = await gs.getBytes32() + assert.deepEqual(h.toUtf8Bytes(currentBytes32), h.toUtf8Bytes(bytes32)) + }) + + it('logs an event', async () => { + const tx = await gs.connect(roles.stranger).setBytes32(bytes32) + const receipt = await tx.wait() + const args: any = receipt.events?.[0].args + + assert.equal(1, receipt.logs?.length) + assert.equal( + roles.stranger.address.toLowerCase(), + args.from.toLowerCase(), + ) + assert.equal( + ethers.utils.toUtf8String(bytes32), + ethers.utils.toUtf8String(args.value), + ) + }) + }) + + describe('#requestedBytes32', () => { + it('updates the request ID and value', async () => { + await gs.requestedBytes32(requestId, bytes32) + + const currentRequestId = await gs.requestId() + assert.equal(currentRequestId, requestId) + + const currentBytes32 = await gs.getBytes32() + assert.deepEqual(h.toUtf8Bytes(currentBytes32), h.toUtf8Bytes(bytes32)) + }) + }) + + describe('#setUint256', () => { + it('updates uint256 value', async () => { + await gs.connect(roles.stranger).setUint256(uint256) + + const currentUint256 = await gs.getUint256() + assert.isTrue(currentUint256.eq(uint256)) + }) + + it('logs an event', async () => { + const tx = await gs.connect(roles.stranger).setUint256(uint256) + const receipt = await tx.wait() + const args: any = receipt.events?.[0].args + + assert.equal(1, receipt.logs?.length) + assert.equal( + roles.stranger.address.toLowerCase(), + args.from.toLowerCase() ?? '', + ) + assert.isTrue(uint256.eq(args.value)) + }) + }) + + describe('#requestedUint256', () => { + it('updates the request ID and value', async () => { + await gs.requestedUint256(requestId, uint256) + + const currentRequestId = await gs.requestId() + assert.equal(currentRequestId, requestId) + + const currentUint256 = await gs.getUint256() + matchers.bigNum(currentUint256, uint256) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/Median.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Median.test.ts new file mode 100644 index 0000000..bacfe4a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Median.test.ts @@ -0,0 +1,109 @@ +import { contract, matchers, setup } from '@chainlink/test-helpers' +import { ethers } from 'ethers' +import { MedianTestHelper__factory } from '../../ethers/v0.5/factories/MedianTestHelper__factory' + +const medianTestHelperFactory = new MedianTestHelper__factory() +const provider = setup.provider() + +let defaultAccount: ethers.Wallet +beforeAll(async () => { + const users = await setup.users(provider) + defaultAccount = users.roles.defaultAccount +}) + +describe('Median', () => { + let median: contract.Instance + + beforeEach(async () => { + median = await medianTestHelperFactory.connect(defaultAccount).deploy() + }) + + describe('testing various lists', () => { + const tests = [ + { + name: 'ordered ascending', + responses: [0, 1, 2, 3, 4, 5, 6, 7], + want: 3, + }, + { + name: 'ordered descending', + responses: [7, 6, 5, 4, 3, 2, 1, 0], + want: 3, + }, + { + name: 'unordered 1', + responses: [1001, 1, 101, 10, 11, 0, 111], + want: 11, + }, + { + name: 'unordered 2', + responses: [8, 8, 4, 5, 5, 7, 9, 5, 9], + want: 7, + }, + { + name: 'unordered 3', + responses: [33, 44, 89, 101, 67, 7, 23, 55, 88, 324, 0, 88], + want: 61, // 67 + 55 / 2 + }, + { + name: 'long unordered', + responses: [ + 333121, + 323453, + 337654, + 345363, + 345363, + 333456, + 335477, + 333323, + 332352, + 354648, + 983260, + 333856, + 335468, + 376987, + 333253, + 388867, + 337879, + 333324, + 338678, + ], + want: 335477, + }, + { + name: 'overflowing numbers', + responses: [ + ethers.utils.bigNumberify( + '57896044618658097711785492504343953926634992332820282019728792003956564819967', + ), + ethers.utils.bigNumberify( + '57896044618658097711785492504343953926634992332820282019728792003956564819967', + ), + ], + want: ethers.utils.bigNumberify( + '57896044618658097711785492504343953926634992332820282019728792003956564819967', + ), + }, + { + name: 'overflowing numbers', + responses: [ + ethers.utils.bigNumberify( + '57896044618658097711785492504343953926634992332820282019728792003956564819967', + ), + ethers.utils.bigNumberify( + '57896044618658097711785492504343953926634992332820282019728792003956564819966', + ), + ], + want: ethers.utils.bigNumberify( + '57896044618658097711785492504343953926634992332820282019728792003956564819966', + ), + }, + ] + + for (const test of tests) { + it(test.name, async () => { + matchers.bigNum(test.want, await median.publicGet(test.responses)) + }) + } + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/Schnorr/Schnorr.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Schnorr/Schnorr.test.ts new file mode 100644 index 0000000..536b96e --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Schnorr/Schnorr.test.ts @@ -0,0 +1,95 @@ +import { + contract, + extensions, + helpers as h, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { SchnorrSECP256K1__factory } from '../../../ethers/v0.5/factories/SchnorrSECP256K1__factory' +import * as f from './fixtures' + +const { bigNumberify: bn } = ethers.utils +extensions.ethers.BigNumber.extend(ethers.utils.BigNumber) + +const schnorrSECP256K1Factory = new SchnorrSECP256K1__factory() +const provider = setup.provider() + +let defaultAccount: ethers.Wallet +beforeAll(async () => { + const users = await setup.users(provider) + defaultAccount = users.roles.defaultAccount +}) + +describe('SchnorrSECP256K1', () => { + let c: contract.Instance + const deployment = setup.snapshot(provider, async () => { + c = await schnorrSECP256K1Factory.connect(defaultAccount).deploy() + }) + + beforeEach(async () => { + await deployment() + }) + + it('Knows a good Schnorr signature from bad', async () => { + assert( + f.publicKey[0].lt(f.groupOrder.shrn(1).add(ethers.constants.One)), + 'x ordinate of public key must be less than half group order.', + ) + + async function checkSignature( + signature: ethers.utils.BigNumberish, + ): Promise { + return c.verifySignature( + f.publicKey[0], + f.pubKeyYParity, + signature, + f.msgHash, + f.kTimesGAddress, + ) + } + assert(await checkSignature(f.s), 'failed to verify good signature') + assert( + !(await checkSignature(f.s.add(ethers.constants.One))), // Corrupt signature for + 'failed to reject bad signature', // // positive control + ) + + const gasUsed = await c.estimate.verifySignature( + f.publicKey[0], + f.pubKeyYParity, + f.s, + f.msgHash, + f.kTimesGAddress, + ) + assert.isBelow(gasUsed.toNumber(), 37500, 'burns too much gas') + }) + + it('Accepts the signatures generated on the go side', async () => { + f.tests.push(f.dssTest) + for (let i = 0; i < Math.min(1, f.tests.length); i++) { + const numbers = f.tests[i].slice(0, f.tests[i].length - 1) + const [msgHash, , pX, pY, sig] = numbers.map(h.addHexPrefix).map(bn) + const rEIP55Address = ethers.utils.getAddress(f.tests[i].pop() ?? '') + assert( + await c.verifySignature( + pX, + pY.isEven() ? 0 : 1, + sig, + msgHash, + rEIP55Address, + ), + 'failed to verify signature constructed by golang tests', + ) + assert( + !(await c.verifySignature( + pX, + pY.isEven() ? 0 : 1, + sig.add(ethers.constants.One), + msgHash, + rEIP55Address, + )), + 'failed to reject bad signature', + ) + } + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.5/Schnorr/fixtures.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Schnorr/fixtures.ts new file mode 100644 index 0000000..5a113b9 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.5/Schnorr/fixtures.ts @@ -0,0 +1,457 @@ +import { extensions, helpers as h } from '@chainlink/test-helpers' +import { ethers } from 'ethers' +export const { bigNumberify: bn } = ethers.utils +extensions.ethers.BigNumber.extend(ethers.utils.BigNumber) +// Number of points in secp256k1 +export const groupOrder = bn( + '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', +) + +export const secretKey = bn( + // Uniformly sampled from {0,...,groupOrder} + '0x5d18fc9fb6494384932af3bda6fe8102c0fa7a26774e22af3993a69e2ca79565', +) +export const publicKey = [ + // '>>>' means "computed in python" + // >>> import py_ecc.secp256k1.secp256k1 as s + // >>> print("'0x%x',\n'0x%x'" % tuple(s.multiply(s.G, secretKey))) + bn('0x6e071bbc2060bce7bae894019d30bdf606bdc8ddc99d5023c4c73185827aeb01'), + bn('0x9ed10348aa5cb37be35802226259ec776119bbea355597db176c66a0f94aa183'), +] +export const [msgHash, k] = [ + // Arbitrary values to test signature + bn('0x18f224412c876d8efb2a3fa670837b5ad1347120363c2b310653f610d382729b'), + bn('0xd51e13c68bf56155a83e50fd9bc840e2a1847fb9b49cd206a577ecd1cd15e285'), +] + +export const kTimesG = [ + // >>> print("'0x%x',\n'0x%x'" % tuple(s.multiply(s.G, k))) + // note that a 04 prefix was added to denote that this is a uncompressed point + bn('0x6c8644d3d376356b540e95f1727b6fd99830d53ef8af963fcc401eeb7b9f8c9f'), + // note that the leading 0x hex prefix was stripped + bn('0xf142b3c0964202b45fb2f862843f75410ce07de04643b28b9ce04633b5fb225c'), +] +export const kTimesGAddress = h.pubkeyToAddress(kTimesG) +export const pubKeyYParity = publicKey[1].isEven() ? 0 : 1 +export const e = bn( + ethers.utils.solidityKeccak256( + ['uint256', 'uint8', 'uint256', 'uint160'], + [publicKey[0], pubKeyYParity ? '0x01' : '0x00', msgHash, kTimesGAddress], + ), +) +export const s = k.sub(e.mul(secretKey)).umod(groupOrder) // s ≡ k - e*secretKey mod groupOrder + +// This test was generated by by kyber/sign/ethdss/ethdss_test.go, with +// printTests set to true. +export const dssTest = [ + '5a94e2a971dac470c2cf16cb4379c386ec9ad79855ec5d9ab7f5bc1d1c303cb8', + '1795577ee8b07abe72205af08699cad9c559f1f9d975c97a35932778ba9a170d', + 'abfc4ff682c5b89495dbf6c73ed981aa447f826a6061f8391532aa2653f97709', + '05b977c36b515544e65b51d477fc16e0123106899009f4879975ed0b043b8859', + '9de9eda4cf2e5539836b82cfe166691fb40a51b8', +] + +// These tests were generated by by kyber/sign/ethschnorr/ethschnorr_test.go, +// with printTests set to true. +export const tests = [ + [ + '3743882645b07f80ff75d9ee843ab1f6206b3880b23a3ba70b06992744cb4f40', + '5cd18fbb15ef224c8fca81aacffbcb34e1677a03af538f39d850ebde353c5952', + '434c824de5d1054f7b96543e9775f8b01de815e4de8a349cc709e2ea2c480ba1', + '45e29dae9c09f8eaf03792da1a19dcbab5343725ea3ac0e356fd12dd405e4bac', + '7b95868caa7fe14af5c941afcb7e19e475fa5af8db3d520619163b1f7903ec5c', + 'f29310502cc0c8b09ba130e4a5b43bc8cbfe93ac', + ], + [ + '3f57b9a5ad1d4fe7cafba1869e371458888b1cc0c030966ff5b84e189aeca7e6', + 'eb2de2311cde029252f76939f6a1cee91344f4e9113d0c3991483068cf2dab08', + '7a6a0b55950fedea3c2d556a17ec46e313348c43249d70ff277eceb8e192eee8', + 'eb8ea82f123c40414fe42941e7a1849fa405cb3b96a93939d3a52117956b5ce4', + 'b0086f4babc2fac25565642451618ca904227eab67f76eaaceafb0857082107b', + 'f66ad597f52d8421927e0b0c912c531289218250', + ], + [ + '8ad923598598a9d1a8faaab3e66fb3b3769d2fffd53ba11d491e03d6a391ae06', + '2ade4b549316339ef25cd64d73adf953e48059f557636f82d5990ce23c36e7a9', + '5c42c7231810a17e0f60e9e28952e1400690f7c00f8044114c28ec1ec3e95023', + '12a2216d27504c92bf7f329b72cda04975fa986d4b9bf01f6e99ab149847ceeb', + '88a073933d42f02bd362103a8df7655cd878eae9dcb367d1b92054d561387fd9', + '1e440c3d8e3a205c4563f4446b1f62919e5452c6', + ], + [ + 'c86f2e3597045cdd37d202bbf8d58128f6cda4c297e810b1612709e737ad8e28', + '8652bdd817f42c769d57bad2f2d0feed342185b78d48a941635c0d337f47780e', + '32255a2eea8c0f8d9f6367fc2f0febe2fba28a341909fdccd9a0fa277e1a249c', + '73b199249ca790f749c343b20882eaa463a8af8e75a93c86beaa3532a6b33076', + 'bea6d15b1b264643f330a29853db17a3fd9fd504c63a36fe8315f67c6325ee93', + 'c14e0e1020d65667c6d62691a3f4a0080ed9e5a7', + ], + [ + '5c3df0399e2a7344b4cb9314bff12b71d8ad4350504b88a588e10997cfff9e5f', + 'abdcb5a1d6cc65ee4eb229836db5be308ea0c40b162a19debb938030b9fbee02', + '1898f97034a3a0f2db6f411b5db4b604f7b96b5bffc0c1acdf6c17ab6ab10055', + 'f30c98448925127e301d63d84dfa1e2a673a171b4165950d138a6eab14dbdaea', + '34344e47aa9c3ba01f662d4d42316154bac02a5429ebaf5c07feb38ed20cbdb9', + 'd9e698c287d7725e97b1f67d2c786dd4782fe487', + ], + [ + '675f62d8c1b84abfa30c42c918e4c18e1d4b177d6bc6ce280d59213c69c5c1b6', + '2092a280f93a5d6aefc81c8ba452b81e48b650c964b76867614c1051fa84d1c8', + '33a0ebe4a02b557a566424488ebb6b0bdd11f47b60aa4e842d357c7ca661d169', + '50989ed974280f57d8c46158e031d8d00bd3faed1c3387694cc4e1421f67f366', + 'e442b80f6b441b4701924b8a5ade9e63dc217526e7030fd76ac459bce629a0da', + 'd814228d2fff81ea76d698698e0cbf2b1ca8d80f', + ], + [ + '0a8a9782dc44b98ab371a2724ac17c265752cc83a3f1954d60ae40e495912196', + '06bb15f055999060b29661378b2d3e3584fe53f9664daefa9307f5a0d471f164', + '35cdaa28f74e5f1912885562490d6cbbb581b0868ae4e0cdd123611df1291c36', + '29cd470f288bdcbc98c62dd7d0da8dab3d43db2c809e7707076ce9188915807f', + 'ec02f2a0e2a17959940950adad631d6466023430d3aa0b82a13ace2b0d0ee08a', + '52178b4f52ba05bd1de4bf87e67407ded4371e06', + ], + [ + 'db350fbdb0b1f0358e4f63789f2c4f8f7450149bccb7efb155b8a5109e90cd95', + '47f5a234da2fd817f8ce1f679e1084de659cd6f8e80e53abfbef88b2fd89c0be', + '6eb7b07fe53970a32f4092aca63dd38bdd7857ca679f9b1351a0ddc8204b42b3', + '30031aa20eceae3f8055c382e45edcd34000e6acf3fe72ac2d5ee861e03e6150', + 'aa8444b4e56e3e740b43eba3d3992c564841683c54ae525030e8e9b3c184d8cb', + '252db651babcb648874081ed5fc69a314f101cbe', + ], + [ + '5cbe9e55912e5c1dc1886f961f97c4007291365b68c4c4439cf3a11ea8bd3d35', + 'e745653c7f68dbdf31fb29a2bb0fce2fb293b3cd832c6b6cd3b427fa8247e19f', + '1eebfb01eb6a55723ea39850d5a262d8fcb074d5d29a6ce7f4e5b3445e21a880', + '7e08471c69a6a7a1f9df870166934b3bcfe910985c19f5c0c9bcf0c4b4be0bf3', + '10a579a3244fda6f5af63573f79dfafae423b8807d8f945fa27961a35bb1c6e7', + 'ae4708d31717a64e743276f5f73b9e86ad86b260', + ], + [ + 'cc45c93a3b06277e3685c25ee5a37c5175a81dbcd5bb52526016689781df009e', + '8c9df550be670929f7ab81da1ab5e2da0c5f24eb648ab8a1c8cdd8fb0ba0e547', + '5c3ca1049ead110591de83baba81fc28c257c7e8e8af42e99ea192a9fb7339e1', + '544e999a44fa2781e67aad1acac9e93c1edcd85627a2e072ec7dc870d6f86411', + 'bab712e38163ad7842213a1c3c04bb67735303924083bccef732aead1e5ce617', + 'ba56fece41e460cfdb84b6b72b170fce26ba7b71', + ], + [ + '7708a9f88c164ec3d132f1432ad7f0b7360acd50959616450c9a5c956a6659f9', + '8b9b1625e83ad3e5c3b4aecb9aafd9460365c11f0a6425742f8bc8b10d86a24b', + '6d0bb2b767924aab514e126038929afac39798de31027be3dbc1095b587057eb', + '6f878cb8936642291c0e877933876548b2f5d3d975797cae931863fca2875d70', + '4378e435d93166368cc6a0a8e3a96e9389ec7e937174217b82943be8a4da7718', + '2ed947ef71e7b46b2977211683b55596ed9358dc', + ], + [ + 'c571b2ef6a3a73c55341e844ef1035ed22f22e901c946bdb69d91a99b9cab551', + 'b93a37930ba5972014d61ec80397167a92a0b4af20277d1d2f0c4e71ccd9e5ce', + '0a7e9521827c2ddf83480c310d502a9d0f7f0df573295cc99cf7c1419ef851f6', + '05ad7cff45cdb385d659fc47d9e00a219ac96b13fcd3584ad5be32d9b8e365d9', + 'aa0f7ace28ee7a83c5ab0c2e9b8bdd92b13993cc7ceb4f3ee7e4231776c39b1b', + 'c8639ab8b1afcb1a4ab9891908387c483c6ffdf3', + ], + [ + '96cfc6576a7e84db9f0f29fb49c411e01a1b5d9daf33cb4e4d6512ad94df3887', + 'd6372083aac9da346ae00e06abc27f461400cb11e61aa4f963ccb6d6871274d6', + '7c5f9723348f8ce785ea94c04f81724acb83ae9e78434cf5ad231147d8d9caca', + 'bfc0953244b7ed8dd5b7af50bcfa96ca3a2cdac027a21a0373ad896ec45706db', + '18ad92cca7cafc8f02d49ecaba595c4abf78be4faba4bd9fc35c191aac007183', + 'd1e2b3539869eb3fdcce8012d31229206959276d', + ], + [ + '479a1b48adc7c2fec4e7cfcca3ddc4c41ccd01aeae2a9d6f106a599bc0b6b236', + 'b27418334f79cb2c3a43cd1be416953dc023654aeeb7f9611aed583c21b6eb1a', + '56a7ab2080e1fdd5907c185b6628cd5d222050710796e66e66221485d521aa55', + '057064c064b1b560d959b07435715571ee403d4039ef60a095c06dbef3a1af94', + 'c32786b17d5e9a3e3d18fc62a317e959dc5ba1184d1b0067742e8d6de928a78a', + '02e5aa9787585bd869ae9ed31c3e67e65b404ed4', + ], + [ + 'd49f997892194e3a9ec4edd7dfa3485ffed8d7e8c7facb6f04de708f9af5eb96', + 'ddd778f7d6b669fc204367192350778b99fca38e3c30c0bd01a19a65b6c5d185', + '2bb43bca185c337a9e28ac34d4ee400c2021c167924f55926ae5b2565f059765', + 'dba3d006b4d425a55eb1c45298b0592f77a95af0f1e6f74f87b267be707af853', + '9acd936be15e86f919343b0ca003b88c7f21691e91a52d5b03bc9c5ebf190d53', + '77e76cdf42df7f81c2e780cac1736d1fbe73d45c', + ], + [ + '44e8adeaff99d62dd404041acefb9fa70f24391a39a9404e7c0b2cbfb6a25d03', + 'b41c1d665a7b6fa013091832c366652917ba109aef68b5d995412c6ee9026b55', + '487dc4869f391ddd4a4856731e3cf4b3f5664e50cfd71534189546f42faab20e', + '9ca895c3ca33d87954cb523f6bb8377368cf834c886aa0521873360f076b09ef', + '622684d3b1b6d8e75a499b6dd183f7eb77383217dfdd16739ecc0361890caf2c', + '37d3c73b48c08494d992c6b4b8af73550ba49f01', + ], + [ + 'bc1697e3efe79aad3e9db96d8d653667839882ec925b858fa3de51d368e89363', + '8fd788210948215775d08137bf80a31306fe3281434d1b6ddb09568b89ebdd94', + '23b50fab978229a30c6b31dc0b0ae92012371fe565d9418520ff971796c66b1e', + '1535fb156283ddcb5baf4ff1ddc492f80f9abd791cb35d8c4782b4542a986c4a', + 'effafcf0fa0aab5cc5a0546b87636dd24edd5644c31da371e605d403d742b061', + '4830690ea3a916d6072ab45a923bde2900d8e10f', + ], + [ + 'e9989b541466394919318d92bf34dbba3fb271cf9f6f998091a0e30eb9af1958', + '7b7e3d4ce529d3f6e8c242e258d726186046e39a0540c83dc88287b253a47b16', + '0f06f7429fc688050e3c5b4a9db22b7731e0fe46cd958739cc19592416b1eb5f', + 'a3cd642d87734dbbe2e70f796da365f70fadb6c53e5fb30350961ec56d4951fc', + 'bb63c3899e5ab0a0a11ef3cdd274d01ef50410ada52953af54a22a37148d53f3', + '1a17edad833225b2f957b96a2cb82ef3b8e190e1', + ], + [ + '1c02be153023e6a8f97cf3670dd7014a4303c98ca25fffed14bca1904dfe595e', + '0f7f7eefc1f84f705945365a0ce3bc856e41b8658656f34e25b19ef0970047cc', + '1952f1f37244037abfef69c6bc255123e689300d3b5180c1282a8c02a91ad4fe', + '40d6760e4fb1962d4efbc51caf0e3fa0b5203901fcbd953d65e6aeb055a6b1cd', + '8afe8824d6d45671ff46aade149d662f40426786be2315f353ae0ffb653ddbef', + '9d70de5bfb2ebfc60d499f9cc03607e33d0a74e0', + ], + [ + '7ebc4a72199237b619cb00f1e1a8d4e692beda87e2a6f2a4e7b8399c2ec96910', + 'ecc01debd6a7d1cb5b7340e67b3251c85132f6eb516ff58c8e48a737ce948e17', + '3ee8013e65e413649568c5d4a6c821728876a30fb8fa26cf88b52162a13e37c1', + 'bb42525d2dffb03e6734c06857718a698af2afef4329641ce551c6d2916f68b3', + '14cea34c380ff5ac0c25d0d3833373861792eecb78104af1bc6016b353f550a1', + 'c822fc3876ede1e2c987af74c59635b18a2500f5', + ], + [ + '82b5ce078350ea12a996afcbd8cfc1083f6a44db4071d2c8486207af97028e1e', + '794989a3ba8e181b0a62abc57806fccf921c5fcc5e6f6b01f27735220eace6f0', + '17b1c0f2aff8e81cbd9fc29280aba91ef063077db4168bf26bbeb18fcbdeb02a', + '1cc87a1583d715a75a93bb8cdc4c6cab5ce6ef95174a166e80f7dc43ee6f7a94', + '31f4cbfb469a47eb30361b50454db20de1259e0a6a0f9cef41f080be63712f88', + '7dfa5aabd08e603c3a571a8bf1a8dde67f84dfbe', + ], + [ + '3ecfa74e5721d0137cad364076bcc0c01a27cd16c6e412739c930d71b01667f0', + 'de72bc5131a127ad968850c0329921e6d65aabe32516a055b18d736171e26d52', + '7de0452e31f3ec4bddd09dc25fa09b69a567c4e16de16c24c214b6aaab7ce095', + '83ba19ab1173f6a41964c3e6f5ed14d53626d0f2e37c025b46a229f479fdcba6', + 'f4859afc9731afb41e647ed723a85c3159022cdae63403fbbdcbc620dee575ef', + '551817fe528ca86204c489d1d2f46707b8aaecca', + ], + [ + '4b4a5da52228b99d760298e97bcf2a95e839b2ca73026ad7200439091a1e5271', + '49c4cc4e4c4dc31332128ae46c6bd11b4a81dde2c178952eda95958f731b6c80', + '4461e03a8756f8f9bd917e295a5c7995531b8b83510e7f025f19225a607e1156', + 'fe6ac9e5bfb9a2042b3569f55da206eee8bb8bafb7f54330b88c35084a31f5d1', + 'd3d324b370bb2f17a807996938514284dbf281f62407977438dc900767954b5d', + '67e6dde5a6e446f73a9aeface8cd73ce427b6d6f', + ], + [ + 'a586144d35215947fec424ce581a686418056b7436aa83be13eab45654069942', + 'e8195518188ed742f0804286cfecf43928b298079dc83fc3094070eb1c47dcd7', + '14d39f461db51a840dd2baa21e8fbcd6a2a367345b95aae428f7e1092a3462ef', + 'd2d2dc345c051b7ff8c59960d9f2feb8542fce41336480d6849ba37cf4e4fbd0', + '739abab8e8d441398ec2225f97a7317c5136347c382cd1800c454850b7a2cb3d', + '8aeef15521a014ee7644f56154d641a78eca4d12', + ], + [ + '48c0caf1b5427eb7b38e9aa9d56344fd086195cfb12184e0288a3508f5dd1ad4', + '202f4075c780c3e532cfe6ca33a6ee0659ebe06d1f2ace377527bd1668b362ac', + '1e53767a3680491372488e4d54dd5cf3c274e49023bda7f09314c217c8b6b393', + 'fe84c90ec2e31c453351ba109b4b7f722d5961b0e1bfed3d978c2d43fad4a220', + 'a230222ab226b12133b67326b4731062cc86511b22e814d487b7afce7959789d', + '414fa675fc4930ef592d3957695f12b90ce1ba97', + ], + [ + 'f301515f29f1c66f427a7af5c0bdc5964f3ac6f208d0a66fcd73392d5c573082', + '1f9c26c4116ce2f718ae82e3ac6a786c25e51630b8d35c475c1404fb9cb71539', + '3928f435af5459dd3f282abe02b6384688956ae7000a5200c45dccee233e6255', + '7a0700afcd360d5abc6741b803aeda63f800ca056e624082be636dc9e50fb2b8', + '529eb7dd4d3d217f7a2af827b63e399d17cd716f6a6234ce7367fc77e24d5f3e', + '534d67cc51180df0604f925d5f2de4ca2b0a987b', + ], + [ + '4c21aaead051914e099f413127e5eaf04fc4f305feffd15aee1221a1250fba6d', + '356c9aa5a9179cc564df6e8a421b408b4986f2204b60e7e900a78537b8febf76', + '21ed8859d849f9672a0abc85040487c78127fdee322a1ecba41e90a9900e265a', + 'a5f0762cd9b529bdd436769d4df0ebf05542260d783ef0bb72b5577f70c4cb52', + '6098adf8afaadbc99752c45943017a487143fad9a8ab4fbd70aff6ffb9cc9609', + 'c42e0f6a4250451724a14678fbb8ca50dcc063e2', + ], + [ + '367482a8761efe8ca49c2e9731037c3c640217cf871712a7730eee33f402abb1', + 'fa958b3efb5354f9f2243a6e4c9e2a22d864a4ab657293bd53e46efe5ac48af4', + '617b8ae34e539cb5c145a8919ba3144026a640b0ccff843f44dec4f1b56b1cf6', + 'e666f496f0319314127b6c6b4483503c89226167a5164bc9202c4d27a921cb88', + 'a04e43e650ba773676dcc42a5974b0d0b408978c991088c0453bc5bb20fc14fb', + '158d1c7916e633619cae155d77aed6fed9606590', + ], + [ + '305fe035b2dc67605133502be815c8111be6cbee2eda7549c74234dbae4a98bd', + '5711a4f58f4cd1fc607c19b2655b13e4a3d1587650631b475067c3c4df33c005', + '0524b9f3e185e69d56c2cd3dc451b844b066266c6bfff2525957a3ddf887b8dd', + 'fd9394f76c5f64f8fd3421d0bd503684ab3fc80fe85833ad43d56cf06df9cfe9', + 'ba9f67e343d25a67cdb2346fcbae9144236708c8e643fdb01c61066167d74b28', + '8437a14f00086d6fea454f6668d061a167bf304b', + ], + [ + 'b603ef4e1643ec6247a7218309bc41eabc65fb4f87f47a9075dda90301e954bd', + 'ce6ca6abafd84200aeb3d503db0eb685fb346acc704176584937651565564fea', + '0f7f222acae4a9886ca8252c4a1d9df2838a020dd7bb8e07dbc41adc02fc4f9f', + '9e33a33170abeb01b806eeca63fc397e867283380564f865e65f8a1f31519309', + 'a3428f3949769251e3c2212d6fc6b96ada02a844f2d9811fecd237b6bc3e0371', + 'dade4783e898a1107642a43d88649e5d3727efc7', + ], + [ + '27c7203ce8c56921d300f717de9f97d7d60b26ec829cb7781b646db819039a3a', + '0ab0099dc7832d017fa5d0ef3d5ea371ed0d7c2fb3dcceac434a7cd5a9a11175', + '360eba8613123357ce01fba538293307572524a1e5dbb72760e14eab77aeb801', + 'ffa9e436d38a412df36308745716605b440b730a7a25762da1f0a5baad79fb43', + 'f9070d5aadbd0e535e828b17137b33e61154733cc79c664352392a291a3a3caa', + '850076117d8514659e829a8bdc3569bc14ef0fb1', + ], + [ + '145a73a8a8f9d1ed9c913756b2d90d5e9df3f6a2a58a912e6a79b55f9d5d6ee4', + '65aae284db1955b0cd613fe300c95db23b0092450a6c9f7e60a630839a2f8272', + '49a40aa05d3dd88265c28484bd200619fadbe83241b38f1ee2c80c5855c8c9ac', + 'f851d0a490cd68a31ad6a7af17ae9b2a8c173262209b552395f1448ab6246840', + '07a65afe033e7f4daa37b8d019fec978fb4b15f156a02f5757ea7644371e4c73', + '58087cf42fd83e9ac0c89302204a65796a8a57a2', + ], + [ + 'a55f4fee5a68a15fa2a735e64eca2df61176c9cef9f638d37b3827c986837f9b', + 'fe53116a81306da505f55923d12613f43e112588fc52ee0ab4f7f30168da8a5d', + '55d6fe044ca5cd660a7438bae00914df3d541b1fd5eaad2c931f4dccd891e513', + '18dcb74c5f705d69768a6c2dbead8a573d6b8a5852b8551be19005f4c2de51a0', + '47cb33bb9f03ad422e3c0096850a80cea3576c0494731df41311846d8dbeaeb1', + '3d9bfe381fe1fb2239a6d09752b03f7de33a5d71', + ], + [ + '5e73d2e6845a64fb2285e73c151d7fddfc162076aa9695b886df0778222c94ff', + 'd5403ea589e53eec05f589d4565058299bcd529ae2f7c5378964108a5e7421b0', + '56c4409d350645fd77ea4dc4f456f9837868786178481b67631bd51504b1bb4f', + '45ea23cf63f94f2c800cf5cff565d2e30a23bd0eaf7faf574570fd07ed10c16b', + 'a97d64abcd20c099bad4009a77cde9e04cd03c33c7205ba27c68c2955c3acfe4', + '6f3756771c5fd0329ce0bfce6e0a8455c5635316', + ], + [ + 'cbd5ba836fe886132d3eeca76dd1606ab472f15678cce0be0bf3d4161c17903c', + '45ff9bc1d469fb6874840a93ea9c65011ebcb898302104a0c33958f86001e856', + '3e393c28d610578648852179d517f4925b41353bcabde5206f682cd191060f9e', + '01cc7fcae34b6930c629e3737f2d531e8d57e61b7a7e46de05d8ef53578f8041', + '22c6cd2ce2a47205d934429ee38a1ddb74dcee419dd8cb22334b1a35f61247bf', + '12f14bbd7b648f205812769edab439714f5e7f08', + ], + [ + 'e9d2f2d0b73e953957fdd798dc8d190aab252d98283bb1a188da9557ee4ca621', + '26e3fc48ff45fd1d6fd0e8967311117a84a9e2596537548c811ed0ee12f4351e', + '1d6b72d5f269f057eec9f7c95280bbc434d91ad4968072bdc29dca5f601cf797', + '4a1eb7e3e2d02e31e85562bacbf1c103b5dbe6352135dacb9643a4643ab331cf', + '05028046d78ee95241d112521a9582d015d8ee141036feb4e5ba795b24327a17', + '10cc56e3ebb01a6650cf4633c3d89fce4b0c0952', + ], + [ + 'f42c3c31869563346b6a09eae09cfdc14afc19d058672edd8dd9d5adb45cd52f', + '75783447885305f3043db25bacad1129496c1813cb649003ec7960a635dfd8d6', + '1b07724e5cdcd41993e8dbb7f94dda5cc6defcfdb71f9bc7545bd5ca578ce086', + 'a9802809f0a8fb4a9e2a0ee4f68c69b677e030b1648e6333da8d38d2a07d1f8e', + '1934bb478bfa4ec525af965e39dc0be75a2396af48b8e8d3301ac4565e3ce1f7', + '5483bb40d453720c50489347a1f67954d08a6f86', + ], + [ + '8c21d61b0c0ada93981e0daabadf55dfb44f5d6bcfaa47c9a1f8b8594eecc2de', + '41e9e6b0305195ec66b0a8e5d6ba88da00301fed5c24e4b6c123ba65314db276', + '79427c5305c8fc5c547e573af364dfa24cab44669c7e4b412310feb343bc81f8', + '83b52f0a88fb725314ba0bb8bfd857d1dc3e4ae3bcd948f19e636c8ac8da6cfd', + '9a6ee3fd9cf9dfc99e2587aa5d1d144fb16c29612fbd4e81d103f87995b8e4e0', + '92b9baa012c127b256769b7807ceaf05484831f6', + ], + [ + '5b3aed9ab6bf5d7f3e823f3468ebd19824fd8004787699cf8fad88983f39e3bd', + 'ab31d17020e388a4a73443073d9b4e4c7ce66208743a2b8b91138e556b4022c3', + '461764d2532ec4118ad14ba8fdeb12e71739e44c98ba7b6c0226fa6a73ebc3d5', + '5a872519732132cc706d4113708ea59b18b229e18bd4d0e2df9d832bb1120832', + 'dbf6322f9d1a9b85984af7789f3529377b74784e38af3c625c6058bd2b0c2f94', + '8862319fe370f9740d90d220f7f25ca69466ef70', + ], + [ + '02880674d3b8d43a6f7011bc7d2212519cb5e97152917815a83b5cce68e8ce6e', + 'f85c7806e834e3c7b36283d83b30fb306d5ad91d54c0680f606ad4be09969e19', + '59957f31043cbfc88d69fb52ab10ff6c3969e1f5389e48cc19d7280d79500995', + '453b3ac7378da208ad29164e7419c80213e84ae4776bd5296e6bf137fd6d7734', + '5dced1bdc4aa85646e246fbc59d93188fe295761669c76f8598190bbc2c6771e', + 'caf7c770c64308324611cc820d7ec564093c8ed0', + ], + [ + 'a993b6b088b1bd3204df596eb51b1f40be3aff016594a2e57da19d2799a6b093', + '0952ec73b69269d61550ba76407a56b9d6d63fa1d1050b9e541580f78481630c', + '73b4723fe0c6a517746af0e4caaddfe03f6753bac56419d996bdbb5a68b1c22e', + '55c55d13d5ad34ffaa9b1d8e9a090fa6ca24d634f36e874d280c51a9fc78255f', + '1c46982ff9fc8228d8f9b815e132f507655a429231229c4e71544d71f98c1e86', + '7a8f12b139daae882b1364c0f3b45827d9945b00', + ], + [ + 'c76cd4af94bbbbd124281f1e6997f5d341e8f54758dab219b9cd0465e625241b', + 'bf6431484f724201910948e46a9f9284a942932bf02fca26346a5094da0a0342', + '54b934ef255144fd83e2ccb56a88cf0d28cfb528cb4183286564914899829534', + '4703349ce372dc95f261a76ca28431bd9fe5c1d25b657ccfc32c1ce00b29862c', + '5212a2e1cd89dece087ed89713eee776bc01c309fa3fc920e4aebf984dcad000', + 'b9e07ce35f639b71b2e5b0786b32efb0c5ab457e', + ], + [ + '84d594b097a74ae09df90652e9e757647d3010a81bfae08fa5ce701bdd6ab517', + '896b7a7c06d45208d8c2a121bc5710c31d77142779547dde77dcb715a851dc33', + '1b13be0409655aabb2c4ae36cc01e176829030c3c5a3fd376dc35b6c28a38472', + '17e69a33237b20150a3e31a3eb4537680c65f80d0b66e94f45d6586c6a932f74', + '4deca85b8cdd8a62e57f957635a00015ff6fac8c304bffdad9c203fe77ba6015', + '570164493bb16860c7db23b6b65de0aa87423d4c', + ], + [ + '4d247016436bd060feed73ae884ff855e405d70de3933616d2602d2e003590a8', + 'b598c4d966b9129c1c68a2d29dad6c831fad46b5971780de26f09b0cc95b0437', + '0a73689d15910fba895852e3eefabc897517eda16c968d9a5106b628b5b80bdc', + 'fefdbd8ab67df2cecb30d08fd3fc3243ec495eb5590b19fe64ed60651dcef417', + '20cb7697542bd05b469b8c8199f4d8b7f25b0463bbec02f47f72d7e795cf9ae9', + 'f8813b5a510ac2f3eda2fe3a801be59e5f10e1f6', + ], + [ + 'd4971a3b4889b05ec4c9246bdc11159ba9377cac3da2cc3d7d3c8a0e5fdea413', + '195533f1391c6e4e29fc90a2186ccfc4578cdaa52128acc138e837aa076d5688', + '0ef8de630bfbf7c2e4eb9017b9fa36c04a4baeca62e7112a66590b9e49189696', + '4fb32117a6e7dce478c4bddc75ad94ea204f360f5f318568a595398cbfc8c455', + 'a606a8bed7673046a8abc8387790597539698b5d6afddcd3995812abc2d0edf9', + 'd96a248e20ab0be6613aa1e29544748bf69ba786', + ], + [ + '384a1aa530ab05adb8a343f1c8ffd2301723215f5cadd09dbb2c034d23c40bc4', + 'beda976e085c065ba908d7e26ae15b4ce3bd4063136eee5e2252f7aa5f4d8554', + '12dbec2d4873fa1bfb42a12b9abaf064917dcb80fb26c16c911a8dbf0a1cde70', + '2708553d082099d751bdf0ab87978821456c3550cdf854d548c7048f54d09f8e', + 'ddf1d3c09d036470072c949b8caf9d3e251fc19c6346e335f0c6ba00de4965b2', + 'a246e6d821d0d7bd76e92e774d2a47a72e8adca7', + ], + [ + '7252a4bd9ad582937357b19550c9217b697c2aed908af91328d93faa6e12d1a2', + '6f17c2020515fd34e22a4ecb21714b0dd98ac1ce08a5142a064714741b687b9c', + '0f31e39d570601c735a91138187aa95a4af97386b195b5bcbbfb643bf3532708', + '0bd713dfef3faf6a3ac03ee7dea1102f88bd5497938f49ab74f37079d536a3f3', + '5bf4738e6cb8977677e94a63029d7fb6ee50bc6766dfcca4b0531960efe3d570', + 'eb2c2b5b91b2bbdc96dcb05a7cc7a5b5a5bcb16e', + ], + [ + '3d3b59887b39f007c55374b92187a39f3308d72b4dbbf3146174315d98a5a200', + '3238e7f9c97b6f0e82bc0f3b4a61b2cbd7fd693b816b009914d0d8ff612094ad', + '5c2cc42bb3314605e86a3fe5466adf5afb70f50603514bd75397b4062fd44290', + '2fefa900df5581a78382eb497da9ca03ec8a9d429928f44a085c5126d9def141', + '6194f091b667dca3bdcaba87b4f82c5453d16b86411e839423eb75613b2dd8b4', + 'bec9dfc52d801692f3a8577b35387df4c9bb52d9', + ], + [ + '3602a2423e0243156a44206defc3bbde2f1ab107f3030e1690a985a363a26430', + '4a26bf1af40a29893e4273cc41d5bed7c4f253ba9c39febd782e59dff603f31d', + '709eea09bee61ed509fe22dd8ffdcdb5428c66e9c558545ef5b5e9bdfaa8fc85', + '2c99a4c2314dad390787ec20fa62373686c82d2169287e82aa4cfb801181de78', + '486d9d30b213933b504343ab1036051a8bbc0fd1016220bd381ab51f22319ac0', + '17840675ab6639ed94a73a397e9cfea26cc37be9', + ], + [ + '4aff63796a53c67fdb3f79111bac7f512a3e5f594402a7a4050aa28046f69629', + 'd2dbeaec3fc6de7acad14a72705b2ee6a8189331d9bac960825e47301c90ab7f', + '5286d0c804b35366f33dadac46a5e4fb05cb78e741e7fdd0cd5cf63751681d3d', + '9b08f31caa3a5e701246185d965b9a16b96069ec5c616c8ede6e684e8b349ded', + 'eee4571d72f7ed0e8abc8fe3f0a0a31c1673e2d9f0bd7b6a571d07315d0bfe05', + 'fe828ab46b100ffac0ba73b293ff178f0f0e83fd', + ], +] diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/AccessControlledAggregator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/AccessControlledAggregator.test.ts new file mode 100644 index 0000000..15bb354 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/AccessControlledAggregator.test.ts @@ -0,0 +1,413 @@ +import { + contract, + helpers as h, + matchers, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { AccessControlledAggregator__factory } from '../../ethers/v0.6/factories/AccessControlledAggregator__factory' +import { FluxAggregatorTestHelper__factory } from '../../ethers/v0.6/factories/FluxAggregatorTestHelper__factory' + +const aggregatorFactory = new AccessControlledAggregator__factory() +const linkTokenFactory = new contract.LinkToken__factory() +const testHelperFactory = new FluxAggregatorTestHelper__factory() +const provider = setup.provider() +let personas: setup.Personas + +beforeAll(async () => { + await setup.users(provider).then((u) => (personas = u.personas)) +}) + +describe('AccessControlledAggregator', () => { + const paymentAmount = h.toWei('3') + const deposit = h.toWei('100') + const answer = 100 + const minAns = 1 + const maxAns = 1 + const rrDelay = 0 + const timeout = 1800 + const decimals = 18 + const description = 'LINK/USD' + const minSubmissionValue = h.bigNum('1') + const maxSubmissionValue = h.bigNum('100000000000000000000') + const emptyAddress = '0x0000000000000000000000000000000000000000' + + let link: contract.Instance + let aggregator: contract.Instance + let testHelper: contract.Instance + let nextRound: number + + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(personas.Default).deploy() + aggregator = await (aggregatorFactory as any) + .connect(personas.Carol) + .deploy( + link.address, + paymentAmount, + timeout, + emptyAddress, + minSubmissionValue, + maxSubmissionValue, + decimals, + h.toBytes32String(description), + // Remove when this PR gets merged: + // https://github.com/ethereum-ts/TypeChain/pull/218 + { gasLimit: 8_000_000 }, + ) + await link.transfer(aggregator.address, deposit) + await aggregator.updateAvailableFunds() + matchers.bigNum(deposit, await link.balanceOf(aggregator.address)) + testHelper = await testHelperFactory.connect(personas.Carol).deploy() + }) + + beforeEach(async () => { + await deployment() + nextRound = 1 + }) + + it('has a limited public interface', () => { + matchers.publicAbi(aggregatorFactory, [ + 'acceptAdmin', + 'allocatedFunds', + 'availableFunds', + 'changeOracles', + 'decimals', + 'description', + 'getAdmin', + 'getAnswer', + 'getOracles', + 'getRoundData', + 'getTimestamp', + 'latestAnswer', + 'latestRound', + 'latestRoundData', + 'latestTimestamp', + 'linkToken', + 'maxSubmissionCount', + 'maxSubmissionValue', + 'minSubmissionCount', + 'minSubmissionValue', + 'onTokenTransfer', + 'oracleCount', + 'oracleRoundState', + 'paymentAmount', + 'requestNewRound', + 'restartDelay', + 'setRequesterPermissions', + 'setValidator', + 'submit', + 'timeout', + 'transferAdmin', + 'updateAvailableFunds', + 'updateFutureRounds', + 'withdrawFunds', + 'withdrawPayment', + 'withdrawablePayment', + 'validator', + 'version', + // Owned methods: + 'acceptOwnership', + 'owner', + 'transferOwnership', + // AccessControl methods: + 'addAccess', + 'disableAccessCheck', + 'enableAccessCheck', + 'removeAccess', + 'checkEnabled', + 'hasAccess', + ]) + }) + + describe('#constructor', () => { + it('sets the paymentAmount', async () => { + matchers.bigNum(h.bigNum(paymentAmount), await aggregator.paymentAmount()) + }) + + it('sets the timeout', async () => { + matchers.bigNum(h.bigNum(timeout), await aggregator.timeout()) + }) + + it('sets the decimals', async () => { + matchers.bigNum(h.bigNum(decimals), await aggregator.decimals()) + }) + + it('sets the description', async () => { + assert.equal( + description, + h.parseBytes32String(await aggregator.description()), + ) + }) + }) + + describe('#getAnswer', () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [personas.Neil.address], + [personas.Neil.address], + minAns, + maxAns, + rrDelay, + ) + await aggregator.connect(personas.Neil).submit(nextRound, answer) + }) + + describe('when read by a contract', () => { + describe('without explicit access', () => { + it('reverts', async () => { + await matchers.evmRevert( + testHelper.readGetAnswer(aggregator.address, 0), + 'No access', + ) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Carol).addAccess(testHelper.address) + await testHelper.readGetAnswer(aggregator.address, 0) + }) + }) + }) + + describe('when read by a regular account', () => { + describe('without explicit access', () => { + it('succeeds', async () => { + const round = await aggregator.latestRound() + await aggregator.connect(personas.Eddy).getAnswer(round) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator + .connect(personas.Carol) + .addAccess(personas.Eddy.address) + const round = await aggregator.latestRound() + await aggregator.connect(personas.Eddy).getAnswer(round) + }) + }) + }) + }) + + describe('#getTimestamp', () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [personas.Neil.address], + [personas.Neil.address], + minAns, + maxAns, + rrDelay, + ) + await aggregator.connect(personas.Neil).submit(nextRound, answer) + }) + + describe('when read by a contract', () => { + describe('without explicit access', () => { + it('reverts', async () => { + await matchers.evmRevert( + testHelper.readGetTimestamp(aggregator.address, 0), + 'No access', + ) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Carol).addAccess(testHelper.address) + await testHelper.readGetTimestamp(aggregator.address, 0) + }) + }) + }) + + describe('when read by a regular account', () => { + describe('without explicit access', () => { + it('succeeds', async () => { + const round = await aggregator.latestRound() + const currentTimestamp = await aggregator + .connect(personas.Eddy) + .getTimestamp(round) + assert.isAbove(currentTimestamp.toNumber(), 0) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator + .connect(personas.Carol) + .addAccess(personas.Eddy.address) + const round = await aggregator.latestRound() + const currentTimestamp = await aggregator + .connect(personas.Eddy) + .getTimestamp(round) + assert.isAbove(currentTimestamp.toNumber(), 0) + }) + }) + }) + }) + + describe('#latestAnswer', () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [personas.Neil.address], + [personas.Neil.address], + minAns, + maxAns, + rrDelay, + ) + await aggregator.connect(personas.Neil).submit(nextRound, answer) + }) + + describe('when read by a contract', () => { + describe('without explicit access', () => { + it('reverts', async () => { + await matchers.evmRevert( + testHelper.readLatestAnswer(aggregator.address), + 'No access', + ) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Carol).addAccess(testHelper.address) + await testHelper.readLatestAnswer(aggregator.address) + }) + }) + }) + + describe('when read by a regular account', () => { + describe('without explicit access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Eddy).latestAnswer() + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator + .connect(personas.Carol) + .addAccess(personas.Eddy.address) + await aggregator.connect(personas.Eddy).latestAnswer() + }) + }) + }) + }) + + describe('#latestTimestamp', () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [personas.Neil.address], + [personas.Neil.address], + minAns, + maxAns, + rrDelay, + ) + await aggregator.connect(personas.Neil).submit(nextRound, answer) + }) + + describe('when read by a contract', () => { + describe('without explicit access', () => { + it('reverts', async () => { + await matchers.evmRevert( + testHelper.readLatestTimestamp(aggregator.address), + 'No access', + ) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Carol).addAccess(testHelper.address) + await testHelper.readLatestTimestamp(aggregator.address) + }) + }) + }) + + describe('when read by a regular account', () => { + describe('without explicit access', () => { + it('succeeds', async () => { + const currentTimestamp = await aggregator + .connect(personas.Eddy) + .latestTimestamp() + assert.isAbove(currentTimestamp.toNumber(), 0) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator + .connect(personas.Carol) + .addAccess(personas.Eddy.address) + const currentTimestamp = await aggregator + .connect(personas.Eddy) + .latestTimestamp() + assert.isAbove(currentTimestamp.toNumber(), 0) + }) + }) + }) + }) + + describe('#latestAnswer', () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [personas.Neil.address], + [personas.Neil.address], + minAns, + maxAns, + rrDelay, + ) + await aggregator.connect(personas.Neil).submit(nextRound, answer) + }) + + describe('when read by a contract', () => { + describe('without explicit access', () => { + it('reverts', async () => { + await matchers.evmRevert( + testHelper.readLatestAnswer(aggregator.address), + 'No access', + ) + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Carol).addAccess(testHelper.address) + await testHelper.readLatestAnswer(aggregator.address) + }) + }) + }) + + describe('when read by a regular account', () => { + describe('without explicit access', () => { + it('succeeds', async () => { + await aggregator.connect(personas.Eddy).latestAnswer() + }) + }) + + describe('with access', () => { + it('succeeds', async () => { + await aggregator + .connect(personas.Carol) + .addAccess(personas.Eddy.address) + await aggregator.connect(personas.Eddy).latestAnswer() + }) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/AggregatorFacade.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/AggregatorFacade.test.ts new file mode 100644 index 0000000..fafefa6 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/AggregatorFacade.test.ts @@ -0,0 +1,145 @@ +import { ethers } from "hardhat"; +import { numToBytes32, publicAbi } from "../test-helpers/helpers"; +import { assert } from "chai"; +import { Contract, ContractFactory, Signer } from "ethers"; +import { getUsers } from "../test-helpers/setup"; +import { convertFufillParams, decodeRunRequest } from "../test-helpers/oracle"; +import { bigNumEquals, evmRevert } from "../test-helpers/matchers"; + +let defaultAccount: Signer; + +let linkTokenFactory: ContractFactory; +let aggregatorFactory: ContractFactory; +let oracleFactory: ContractFactory; +let aggregatorFacadeFactory: ContractFactory; + +before(async () => { + const users = await getUsers(); + + defaultAccount = users.roles.defaultAccount; + linkTokenFactory = await ethers.getContractFactory("LinkToken", defaultAccount); + aggregatorFactory = await ethers.getContractFactory("Aggregator", defaultAccount); + oracleFactory = await ethers.getContractFactory("src/v0.6/Oracle.sol:Oracle", defaultAccount); + aggregatorFacadeFactory = await ethers.getContractFactory("AggregatorFacade", defaultAccount); +}); + +describe("AggregatorFacade", () => { + const jobId1 = "0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000001"; + const previousResponse = numToBytes32(54321); + const response = numToBytes32(67890); + const decimals = 18; + const description = "LINK / USD: Historic Aggregator Facade"; + + let link: Contract; + let aggregator: Contract; + let oc1: Contract; + let facade: Contract; + + beforeEach(async () => { + link = await linkTokenFactory.connect(defaultAccount).deploy(); + oc1 = await oracleFactory.connect(defaultAccount).deploy(link.address); + aggregator = await aggregatorFactory.connect(defaultAccount).deploy(link.address, 0, 1, [oc1.address], [jobId1]); + facade = await aggregatorFacadeFactory.connect(defaultAccount).deploy(aggregator.address, decimals, description); + + let requestTx = await aggregator.requestRateUpdate(); + let receipt = await requestTx.wait(); + let request = decodeRunRequest(receipt.logs?.[3]); + await oc1.fulfillOracleRequest(...convertFufillParams(request, previousResponse)); + requestTx = await aggregator.requestRateUpdate(); + receipt = await requestTx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + await oc1.fulfillOracleRequest(...convertFufillParams(request, response)); + }); + + it("has a limited public interface", () => { + publicAbi(facade, [ + "aggregator", + "decimals", + "description", + "getAnswer", + "getRoundData", + "getTimestamp", + "latestAnswer", + "latestRound", + "latestRoundData", + "latestTimestamp", + "version", + ]); + }); + + describe("#constructor", () => { + it("uses the decimals set in the constructor", async () => { + bigNumEquals(decimals, await facade.decimals()); + }); + + it("uses the description set in the constructor", async () => { + assert.equal(description, await facade.description()); + }); + + it("sets the version to 2", async () => { + bigNumEquals(2, await facade.version()); + }); + }); + + describe("#getAnswer/latestAnswer", () => { + it("pulls the rate from the aggregator", async () => { + bigNumEquals(response, await facade.latestAnswer()); + const latestRound = await facade.latestRound(); + bigNumEquals(response, await facade.getAnswer(latestRound)); + }); + }); + + describe("#getTimestamp/latestTimestamp", () => { + it("pulls the timestamp from the aggregator", async () => { + const height = await aggregator.latestTimestamp(); + assert.notEqual("0", height.toString()); + bigNumEquals(height, await facade.latestTimestamp()); + const latestRound = await facade.latestRound(); + bigNumEquals(await aggregator.latestTimestamp(), await facade.getTimestamp(latestRound)); + }); + }); + + describe("#getRoundData", () => { + it("assembles the requested round data", async () => { + const previousId = (await facade.latestRound()).sub(1); + const round = await facade.getRoundData(previousId); + bigNumEquals(previousId, round.roundId); + bigNumEquals(previousResponse, round.answer); + bigNumEquals(await facade.getTimestamp(previousId), round.startedAt); + bigNumEquals(await facade.getTimestamp(previousId), round.updatedAt); + bigNumEquals(previousId, round.answeredInRound); + }); + + it("returns zero data for non-existing rounds", async () => { + const roundId = 13371337; + await evmRevert(facade.getRoundData(roundId), "No data present"); + }); + }); + + describe("#latestRoundData", () => { + it("assembles the requested round data", async () => { + const latestId = await facade.latestRound(); + const round = await facade.latestRoundData(); + bigNumEquals(latestId, round.roundId); + bigNumEquals(response, round.answer); + bigNumEquals(await facade.getTimestamp(latestId), round.startedAt); + bigNumEquals(await facade.getTimestamp(latestId), round.updatedAt); + bigNumEquals(latestId, round.answeredInRound); + }); + + describe("when there is no latest round", () => { + beforeEach(async () => { + aggregator = await aggregatorFactory + .connect(defaultAccount) + .deploy(link.address, 0, 1, [oc1.address], [jobId1]); + facade = await aggregatorFacadeFactory + .connect(defaultAccount) + .deploy(aggregator.address, decimals, description); + }); + + it("assembles the requested round data", async () => { + await evmRevert(facade.latestRoundData(), "No data present"); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/AggregatorProxy.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/AggregatorProxy.test.ts new file mode 100644 index 0000000..08caf47 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/AggregatorProxy.test.ts @@ -0,0 +1,694 @@ +import { + contract, + helpers as h, + matchers, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { BigNumber } from 'ethers/utils' +import { MockV2Aggregator__factory } from '../../ethers/v0.6/factories/MockV2Aggregator__factory' +import { MockV3Aggregator__factory } from '../../ethers/v0.6/factories/MockV3Aggregator__factory' +import { AggregatorProxy__factory } from '../../ethers/v0.6/factories/AggregatorProxy__factory' +import { AggregatorFacade__factory } from '../../ethers/v0.6/factories/AggregatorFacade__factory' +import { FluxAggregator__factory } from '../../ethers/v0.6/factories/FluxAggregator__factory' +import { Reverter__factory } from '../../ethers/v0.6/factories/Reverter__factory' + +let personas: setup.Personas +let defaultAccount: ethers.Wallet + +const provider = setup.provider() +const linkTokenFactory = new contract.LinkToken__factory() +const aggregatorFactory = new MockV3Aggregator__factory() +const historicAggregatorFactory = new MockV2Aggregator__factory() +const aggregatorFacadeFactory = new AggregatorFacade__factory() +const aggregatorProxyFactory = new AggregatorProxy__factory() +const fluxAggregatorFactory = new FluxAggregator__factory() +const reverterFactory = new Reverter__factory() + +beforeAll(async () => { + const users = await setup.users(provider) + + personas = users.personas + defaultAccount = users.roles.defaultAccount +}) + +describe('AggregatorProxy', () => { + const deposit = h.toWei('100') + const response = h.numToBytes32(54321) + const response2 = h.numToBytes32(67890) + const decimals = 18 + const phaseBase = h.bigNum(2).pow(64) + + let link: contract.Instance + let aggregator: contract.Instance + let aggregator2: contract.Instance + let historicAggregator: contract.Instance + let proxy: contract.Instance + let flux: contract.Instance + let reverter: contract.Instance + + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(defaultAccount).deploy() + aggregator = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response) + await link.transfer(aggregator.address, deposit) + proxy = await aggregatorProxyFactory + .connect(defaultAccount) + .deploy(aggregator.address) + const emptyAddress = '0x0000000000000000000000000000000000000000' + flux = await fluxAggregatorFactory + .connect(personas.Carol) + .deploy(link.address, 0, 0, emptyAddress, 0, 0, 18, 'TEST / LINK') + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(aggregatorProxyFactory, [ + 'aggregator', + 'confirmAggregator', + 'decimals', + 'description', + 'getAnswer', + 'getRoundData', + 'getTimestamp', + 'latestAnswer', + 'latestRound', + 'latestRoundData', + 'latestTimestamp', + 'phaseAggregators', + 'phaseId', + 'proposeAggregator', + 'proposedAggregator', + 'proposedGetRoundData', + 'proposedLatestRoundData', + 'version', + // Ownable methods: + 'acceptOwnership', + 'owner', + 'transferOwnership', + ]) + }) + + describe('constructor', () => { + it('sets the proxy phase and aggregator', async () => { + matchers.bigNum(1, await proxy.phaseId()) + assert.equal(aggregator.address, await proxy.phaseAggregators(1)) + }) + }) + + describe('#latestRound', () => { + it('pulls the rate from the aggregator', async () => { + matchers.bigNum(phaseBase.add(1), await proxy.latestRound()) + }) + }) + + describe('#latestAnswer', () => { + it('pulls the rate from the aggregator', async () => { + matchers.bigNum(response, await proxy.latestAnswer()) + const latestRound = await proxy.latestRound() + matchers.bigNum(response, await proxy.getAnswer(latestRound)) + }) + + describe('after being updated to another contract', () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + await link.transfer(aggregator2.address, deposit) + matchers.bigNum(response2, await aggregator2.latestAnswer()) + + await proxy.proposeAggregator(aggregator2.address) + await proxy.confirmAggregator(aggregator2.address) + }) + + it('pulls the rate from the new aggregator', async () => { + matchers.bigNum(response2, await proxy.latestAnswer()) + const latestRound = await proxy.latestRound() + matchers.bigNum(response2, await proxy.getAnswer(latestRound)) + }) + }) + + describe('when the relevant info is not available', () => { + beforeEach(async () => { + await proxy.proposeAggregator(flux.address) + await proxy.confirmAggregator(flux.address) + }) + + it('does not revert when called with a non existent ID', async () => { + const actual = await proxy.latestAnswer() + matchers.bigNum(0, actual) + }) + }) + }) + + describe('#getAnswer', () => { + describe('when the relevant round is not available', () => { + beforeEach(async () => { + await proxy.proposeAggregator(flux.address) + await proxy.confirmAggregator(flux.address) + }) + + it('does not revert when called with a non existent ID', async () => { + const proxyId = phaseBase.mul(await proxy.phaseId()).add(1) + const actual = await proxy.getAnswer(proxyId) + matchers.bigNum(0, actual) + }) + }) + + describe('when the answer reverts in a non-predicted way', () => { + it('reverts', async () => { + reverter = await reverterFactory.connect(defaultAccount).deploy() + await proxy.proposeAggregator(reverter.address) + await proxy.confirmAggregator(reverter.address) + assert.equal(reverter.address, await proxy.aggregator()) + + const proxyId = phaseBase.mul(await proxy.phaseId()) + + await matchers.evmRevert( + proxy.getAnswer(proxyId), + 'Raised by Reverter.sol', + ) + }) + }) + + describe('after being updated to another contract', () => { + let preUpdateRoundId: BigNumber + let preUpdateAnswer: BigNumber + + beforeEach(async () => { + preUpdateRoundId = await proxy.latestRound() + preUpdateAnswer = await proxy.latestAnswer() + + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + await link.transfer(aggregator2.address, deposit) + matchers.bigNum(response2, await aggregator2.latestAnswer()) + + await proxy.proposeAggregator(aggregator2.address) + await proxy.confirmAggregator(aggregator2.address) + }) + + it('reports answers for previous phases', async () => { + const actualAnswer = await proxy.getAnswer(preUpdateRoundId) + matchers.bigNum(preUpdateAnswer, actualAnswer) + }) + }) + + describe('when the relevant info is not available', () => { + it('returns 0', async () => { + const actual = await proxy.getAnswer(phaseBase.mul(777)) + matchers.bigNum(0, actual) + }) + }) + + describe('when the round ID is too large', () => { + const overflowRoundId = h + .bigNum(2) + .pow(255) + .add(phaseBase) // get the original phase + .add(1) // get the original round + it('returns 0', async () => { + const actual = await proxy.getTimestamp(overflowRoundId) + matchers.bigNum(0, actual) + }) + }) + }) + + describe('#getTimestamp', () => { + describe('when the relevant round is not available', () => { + beforeEach(async () => { + await proxy.proposeAggregator(flux.address) + await proxy.confirmAggregator(flux.address) + }) + + it('does not revert when called with a non existent ID', async () => { + const proxyId = phaseBase.mul(await proxy.phaseId()).add(1) + const actual = await proxy.getTimestamp(proxyId) + matchers.bigNum(0, actual) + }) + }) + + describe('when the relevant info is not available', () => { + it('returns 0', async () => { + const actual = await proxy.getTimestamp(phaseBase.mul(777)) + matchers.bigNum(0, actual) + }) + }) + + describe('when the round ID is too large', () => { + const overflowRoundId = h + .bigNum(2) + .pow(255) + .add(phaseBase) // get the original phase + .add(1) // get the original round + + it('returns 0', async () => { + const actual = await proxy.getTimestamp(overflowRoundId) + matchers.bigNum(0, actual) + }) + }) + }) + + describe('#latestTimestamp', () => { + beforeEach(async () => { + const height = await aggregator.latestTimestamp() + assert.notEqual('0', height.toString()) + }) + + it('pulls the timestamp from the aggregator', async () => { + matchers.bigNum( + await aggregator.latestTimestamp(), + await proxy.latestTimestamp(), + ) + const latestRound = await proxy.latestRound() + matchers.bigNum( + await aggregator.latestTimestamp(), + await proxy.getTimestamp(latestRound), + ) + }) + + describe('after being updated to another contract', () => { + beforeEach(async () => { + await h.increaseTimeBy(30, provider) + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + + const height2 = await aggregator2.latestTimestamp() + assert.notEqual('0', height2.toString()) + + const height1 = await aggregator.latestTimestamp() + assert.notEqual( + height1.toString(), + height2.toString(), + 'Height1 and Height2 should not be equal', + ) + + await proxy.proposeAggregator(aggregator2.address) + await proxy.confirmAggregator(aggregator2.address) + }) + + it('pulls the timestamp from the new aggregator', async () => { + matchers.bigNum( + await aggregator2.latestTimestamp(), + await proxy.latestTimestamp(), + ) + const latestRound = await proxy.latestRound() + matchers.bigNum( + await aggregator2.latestTimestamp(), + await proxy.getTimestamp(latestRound), + ) + }) + }) + }) + + describe('#getRoundData', () => { + describe('when pointed at a Historic Aggregator', () => { + beforeEach(async () => { + historicAggregator = await historicAggregatorFactory + .connect(defaultAccount) + .deploy(response2) + await proxy.proposeAggregator(historicAggregator.address) + await proxy.confirmAggregator(historicAggregator.address) + }) + + it('reverts', async () => { + const latestRoundId = await historicAggregator.latestRound() + await matchers.evmRevert(proxy.getRoundData(latestRoundId)) + }) + + describe('when pointed at an Aggregator Facade', () => { + beforeEach(async () => { + const facade = await aggregatorFacadeFactory + .connect(defaultAccount) + .deploy(aggregator.address, 18, 'LINK/USD: Aggregator Facade') + await proxy.proposeAggregator(facade.address) + await proxy.confirmAggregator(facade.address) + }) + + it('works for a valid roundId', async () => { + const aggId = await aggregator.latestRound() + const phaseId = phaseBase.mul(await proxy.phaseId()) + const proxyId = phaseId.add(aggId) + + const round = await proxy.getRoundData(proxyId) + matchers.bigNum(proxyId, round.roundId) + matchers.bigNum(response, round.answer) + const nowSeconds = new Date().valueOf() / 1000 + assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120) + matchers.bigNum(round.updatedAt, round.startedAt) + matchers.bigNum(proxyId, round.answeredInRound) + }) + }) + }) + + describe('when pointed at a FluxAggregator', () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + + await proxy.proposeAggregator(aggregator2.address) + await proxy.confirmAggregator(aggregator2.address) + }) + + it('works for a valid round ID', async () => { + const aggId = phaseBase.sub(2) + await aggregator2 + .connect(personas.Carol) + .updateRoundData(aggId, response2, 77, 42) + + const phaseId = phaseBase.mul(await proxy.phaseId()) + const proxyId = phaseId.add(aggId) + + const round = await proxy.getRoundData(proxyId) + matchers.bigNum(proxyId, round.roundId) + matchers.bigNum(response2, round.answer) + matchers.bigNum(42, round.startedAt) + matchers.bigNum(77, round.updatedAt) + matchers.bigNum(proxyId, round.answeredInRound) + }) + }) + + it('reads round ID of a previous phase', async () => { + const oldphaseId = phaseBase.mul(await proxy.phaseId()) + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + + await proxy.proposeAggregator(aggregator2.address) + await proxy.confirmAggregator(aggregator2.address) + + const aggId = await aggregator.latestRound() + const proxyId = oldphaseId.add(aggId) + + const round = await proxy.getRoundData(proxyId) + matchers.bigNum(proxyId, round.roundId) + matchers.bigNum(response, round.answer) + const nowSeconds = new Date().valueOf() / 1000 + assert.isAbove(round.startedAt.toNumber(), nowSeconds - 120) + assert.isBelow(round.startedAt.toNumber(), nowSeconds) + matchers.bigNum(round.startedAt, round.updatedAt) + matchers.bigNum(proxyId, round.answeredInRound) + }) + }) + + describe('#latestRoundData', () => { + describe('when pointed at a Historic Aggregator', () => { + beforeEach(async () => { + historicAggregator = await historicAggregatorFactory + .connect(defaultAccount) + .deploy(response2) + await proxy.proposeAggregator(historicAggregator.address) + await proxy.confirmAggregator(historicAggregator.address) + }) + + it('reverts', async () => { + await matchers.evmRevert(proxy.latestRoundData()) + }) + + describe('when pointed at an Aggregator Facade', () => { + beforeEach(async () => { + const facade = await aggregatorFacadeFactory + .connect(defaultAccount) + .deploy( + historicAggregator.address, + 17, + 'DOGE/ZWL: Aggregator Facade', + ) + await proxy.proposeAggregator(facade.address) + await proxy.confirmAggregator(facade.address) + }) + + it('does not revert', async () => { + const aggId = await historicAggregator.latestRound() + const phaseId = phaseBase.mul(await proxy.phaseId()) + const proxyId = phaseId.add(aggId) + + const round = await proxy.latestRoundData() + matchers.bigNum(proxyId, round.roundId) + matchers.bigNum(response2, round.answer) + const nowSeconds = new Date().valueOf() / 1000 + assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120) + matchers.bigNum(round.updatedAt, round.startedAt) + matchers.bigNum(proxyId, round.answeredInRound) + }) + + it('uses the decimals set in the constructor', async () => { + matchers.bigNum(17, await proxy.decimals()) + }) + + it('uses the description set in the constructor', async () => { + assert.equal('DOGE/ZWL: Aggregator Facade', await proxy.description()) + }) + + it('sets the version to 2', async () => { + matchers.bigNum(2, await proxy.version()) + }) + }) + }) + + describe('when pointed at a FluxAggregator', () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + + await proxy.proposeAggregator(aggregator2.address) + await proxy.confirmAggregator(aggregator2.address) + }) + + it('does not revert', async () => { + const aggId = phaseBase.sub(2) + await aggregator2 + .connect(personas.Carol) + .updateRoundData(aggId, response2, 77, 42) + + const phaseId = phaseBase.mul(await proxy.phaseId()) + const proxyId = phaseId.add(aggId) + + const round = await proxy.latestRoundData() + matchers.bigNum(proxyId, round.roundId) + matchers.bigNum(response2, round.answer) + matchers.bigNum(42, round.startedAt) + matchers.bigNum(77, round.updatedAt) + matchers.bigNum(proxyId, round.answeredInRound) + }) + + it('uses the decimals of the aggregator', async () => { + matchers.bigNum(18, await proxy.decimals()) + }) + + it('uses the description of the aggregator', async () => { + assert.equal( + 'v0.6/tests/MockV3Aggregator.sol', + await proxy.description(), + ) + }) + + it('uses the version of the aggregator', async () => { + matchers.bigNum(0, await proxy.version()) + }) + }) + }) + + describe('#proposeAggregator', () => { + beforeEach(async () => { + await proxy.transferOwnership(personas.Carol.address) + await proxy.connect(personas.Carol).acceptOwnership() + + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, 1) + + assert.equal(aggregator.address, await proxy.aggregator()) + }) + + describe('when called by the owner', () => { + it('sets the address of the proposed aggregator', async () => { + await proxy + .connect(personas.Carol) + .proposeAggregator(aggregator2.address) + + assert.equal(aggregator2.address, await proxy.proposedAggregator()) + }) + }) + + describe('when called by a non-owner', () => { + it('does not update', async () => { + await matchers.evmRevert( + proxy.connect(personas.Neil).proposeAggregator(aggregator2.address), + 'Only callable by owner', + ) + + assert.equal(aggregator.address, await proxy.aggregator()) + }) + }) + }) + + describe('#confirmAggregator', () => { + beforeEach(async () => { + await proxy.transferOwnership(personas.Carol.address) + await proxy.connect(personas.Carol).acceptOwnership() + + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, 1) + + assert.equal(aggregator.address, await proxy.aggregator()) + }) + + describe('when called by the owner', () => { + beforeEach(async () => { + await proxy + .connect(personas.Carol) + .proposeAggregator(aggregator2.address) + }) + + it('sets the address of the new aggregator', async () => { + await proxy + .connect(personas.Carol) + .confirmAggregator(aggregator2.address) + + assert.equal(aggregator2.address, await proxy.aggregator()) + }) + + it('increases the phase', async () => { + matchers.bigNum(1, await proxy.phaseId()) + + await proxy + .connect(personas.Carol) + .confirmAggregator(aggregator2.address) + + matchers.bigNum(2, await proxy.phaseId()) + }) + + it('increases the round ID', async () => { + matchers.bigNum(phaseBase.add(1), await proxy.latestRound()) + + await proxy + .connect(personas.Carol) + .confirmAggregator(aggregator2.address) + + matchers.bigNum(phaseBase.mul(2).add(1), await proxy.latestRound()) + }) + + it('sets the proxy phase and aggregator', async () => { + assert.equal( + '0x0000000000000000000000000000000000000000', + await proxy.phaseAggregators(2), + ) + + await proxy + .connect(personas.Carol) + .confirmAggregator(aggregator2.address) + + assert.equal(aggregator2.address, await proxy.phaseAggregators(2)) + }) + }) + + describe('when called by a non-owner', () => { + beforeEach(async () => { + await proxy + .connect(personas.Carol) + .proposeAggregator(aggregator2.address) + }) + + it('does not update', async () => { + await matchers.evmRevert( + proxy.connect(personas.Neil).confirmAggregator(aggregator2.address), + 'Only callable by owner', + ) + + assert.equal(aggregator.address, await proxy.aggregator()) + }) + }) + }) + + describe('#proposedGetRoundData', () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + }) + + describe('when an aggregator has been proposed', () => { + beforeEach(async () => { + await proxy + .connect(defaultAccount) + .proposeAggregator(aggregator2.address) + assert.equal(await proxy.proposedAggregator(), aggregator2.address) + }) + + it('returns the data for the proposed aggregator', async () => { + const roundId = await aggregator2.latestRound() + const round = await proxy.proposedGetRoundData(roundId) + matchers.bigNum(roundId, round.roundId) + matchers.bigNum(response2, round.answer) + }) + + describe('after the aggregator has been confirmed', () => { + beforeEach(async () => { + await proxy + .connect(defaultAccount) + .confirmAggregator(aggregator2.address) + assert.equal(await proxy.aggregator(), aggregator2.address) + }) + + it('reverts', async () => { + const roundId = await aggregator2.latestRound() + await matchers.evmRevert( + proxy.proposedGetRoundData(roundId), + 'No proposed aggregator present', + ) + }) + }) + }) + }) + + describe('#proposedLatestRoundData', () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, response2) + }) + + describe('when an aggregator has been proposed', () => { + beforeEach(async () => { + await proxy + .connect(defaultAccount) + .proposeAggregator(aggregator2.address) + assert.equal(await proxy.proposedAggregator(), aggregator2.address) + }) + + it('returns the data for the proposed aggregator', async () => { + const roundId = await aggregator2.latestRound() + const round = await proxy.proposedLatestRoundData() + matchers.bigNum(roundId, round.roundId) + matchers.bigNum(response2, round.answer) + }) + + describe('after the aggregator has been confirmed', () => { + beforeEach(async () => { + await proxy + .connect(defaultAccount) + .confirmAggregator(aggregator2.address) + assert.equal(await proxy.aggregator(), aggregator2.address) + }) + + it('reverts', async () => { + await matchers.evmRevert( + proxy.proposedLatestRoundData(), + 'No proposed aggregator present', + ) + }) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/BasicConsumer.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/BasicConsumer.test.ts new file mode 100644 index 0000000..0f83f03 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/BasicConsumer.test.ts @@ -0,0 +1,205 @@ +import { ethers } from "hardhat"; +import { toWei, increaseTime5Minutes, toHex } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { BigNumber, constants, Contract, ContractFactory } from "ethers"; +import { Roles, getUsers } from "../test-helpers/setup"; +import { bigNumEquals, evmRevert } from "../test-helpers/matchers"; +import { convertFufillParams, decodeRunRequest, encodeOracleRequest, RunRequest } from "../test-helpers/oracle"; +import cbor from "cbor"; +import { makeDebug } from "../test-helpers/debug"; + +const d = makeDebug("BasicConsumer"); +let basicConsumerFactory: ContractFactory; +let oracleFactory: ContractFactory; +let linkTokenFactory: ContractFactory; + +let roles: Roles; + +before(async () => { + roles = (await getUsers()).roles; + basicConsumerFactory = await ethers.getContractFactory( + "src/v0.6/tests/BasicConsumer.sol:BasicConsumer", + roles.defaultAccount, + ); + oracleFactory = await ethers.getContractFactory("src/v0.6/Oracle.sol:Oracle", roles.oracleNode); + linkTokenFactory = await ethers.getContractFactory("LinkToken", roles.defaultAccount); +}); + +describe("BasicConsumer", () => { + const specId = "0x4c7b7ffb66b344fbaa64995af81e355a".padEnd(66, "0"); + const currency = "USD"; + const payment = toWei("1"); + let link: Contract; + let oc: Contract; + let cc: Contract; + + beforeEach(async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy(); + oc = await oracleFactory.connect(roles.oracleNode).deploy(link.address); + cc = await basicConsumerFactory.connect(roles.defaultAccount).deploy(link.address, oc.address, specId); + }); + + it("has a predictable gas price", async () => { + const rec = await ethers.provider.getTransactionReceipt(cc.deployTransaction.hash ?? ""); + assert.isBelow(rec.gasUsed?.toNumber() ?? -1, 1750000); + }); + + describe("#requestEthereumPrice", () => { + describe("without LINK", () => { + it("reverts", async () => await expect(cc.requestEthereumPrice(currency, payment)).to.be.reverted); + }); + + describe("with LINK", () => { + beforeEach(async () => { + await link.transfer(cc.address, toWei("1")); + }); + + it("triggers a log event in the Oracle contract", async () => { + const tx = await cc.requestEthereumPrice(currency, payment); + const receipt = await tx.wait(); + + const log = receipt?.logs?.[3]; + assert.equal(log?.address.toLowerCase(), oc.address.toLowerCase()); + + const request = decodeRunRequest(log); + const expected = { + path: ["USD"], + get: "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY", + }; + + assert.equal(toHex(specId), request.specId); + bigNumEquals(toWei("1"), request.payment); + assert.equal(cc.address.toLowerCase(), request.requester.toLowerCase()); + assert.equal(1, request.dataVersion); + assert.deepEqual(expected, cbor.decodeFirstSync(request.data)); + }); + + it("has a reasonable gas cost", async () => { + const tx = await cc.requestEthereumPrice(currency, payment); + const receipt = await tx.wait(); + + assert.isBelow(receipt?.gasUsed?.toNumber() ?? -1, 131515); + }); + }); + }); + + describe("#fulfillOracleRequest", () => { + const response = ethers.utils.formatBytes32String("1,000,000.00"); + let request: RunRequest; + + beforeEach(async () => { + await link.transfer(cc.address, toWei("1")); + const tx = await cc.requestEthereumPrice(currency, payment); + const receipt = await tx.wait(); + + request = decodeRunRequest(receipt?.logs?.[3]); + }); + + it("records the data given to it by the oracle", async () => { + await oc.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + const currentPrice = await cc.currentPrice(); + assert.equal(currentPrice, response); + }); + + it("logs the data given to it by the oracle", async () => { + const tx = await oc.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + const receipt = await tx.wait(); + + assert.equal(2, receipt?.logs?.length); + const log = receipt?.logs?.[1]; + + assert.equal(log?.topics[2], response); + }); + + describe("when the consumer does not recognize the request ID", () => { + let otherRequest: RunRequest; + + beforeEach(async () => { + // Create a request directly via the oracle, rather than through the + // chainlink client (consumer). The client should not respond to + // fulfillment of this request, even though the oracle will faithfully + // forward the fulfillment to it. + const args = encodeOracleRequest( + toHex(specId), + cc.address, + basicConsumerFactory.interface.getSighash("fulfill"), + 43, + constants.HashZero, + ); + const tx = await link.transferAndCall(oc.address, 0, args); + const receipt = await tx.wait(); + + otherRequest = decodeRunRequest(receipt?.logs?.[2]); + }); + + it("does not accept the data provided", async () => { + d("otherRequest %s", otherRequest); + await oc.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(otherRequest, response)); + + const received = await cc.currentPrice(); + + assert.equal(ethers.utils.parseBytes32String(received), ""); + }); + }); + + describe("when called by anyone other than the oracle contract", () => { + it("does not accept the data provided", async () => { + await evmRevert(cc.connect(roles.oracleNode).fulfill(request.requestId, response)); + + const received = await cc.currentPrice(); + assert.equal(ethers.utils.parseBytes32String(received), ""); + }); + }); + }); + + describe("#cancelRequest", () => { + const depositAmount = toWei("1"); + let request: RunRequest; + + beforeEach(async () => { + await link.transfer(cc.address, depositAmount); + const tx = await cc.requestEthereumPrice(currency, payment); + const receipt = await tx.wait(); + + request = decodeRunRequest(receipt.logs?.[3]); + }); + + describe("before 5 minutes", () => { + it("cant cancel the request", () => + evmRevert( + cc + .connect(roles.consumer) + .cancelRequest(oc.address, request.requestId, request.payment, request.callbackFunc, request.expiration), + )); + }); + + describe("after 5 minutes", () => { + it("can cancel the request", async () => { + await increaseTime5Minutes(ethers.provider); + + await cc + .connect(roles.consumer) + .cancelRequest(oc.address, request.requestId, request.payment, request.callbackFunc, request.expiration); + }); + }); + }); + + describe("#withdrawLink", () => { + const depositAmount = toWei("1"); + + beforeEach(async () => { + await link.transfer(cc.address, depositAmount); + const balance = await link.balanceOf(cc.address); + bigNumEquals(balance, depositAmount); + }); + + it("transfers LINK out of the contract", async () => { + await cc.connect(roles.consumer).withdrawLink(); + const ccBalance = await link.balanceOf(cc.address); + const consumerBalance = BigNumber.from(await link.balanceOf(await roles.consumer.getAddress())); + bigNumEquals(ccBalance, 0); + bigNumEquals(consumerBalance, depositAmount); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/BlockhashStore.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/BlockhashStore.test.ts new file mode 100644 index 0000000..2dffea8 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/BlockhashStore.test.ts @@ -0,0 +1,257 @@ +import { ethers } from "hardhat"; +import { assert, expect } from "chai"; +import { Contract, ContractFactory } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; + +let personas: Personas; +let blockhashStoreTestHelperFactory: ContractFactory; + +type TestBlocks = { + num: number; + rlpHeader: Uint8Array; + hash: string; +}; + +const mainnetBlocks: TestBlocks[] = [ + { + num: 10000467, + rlpHeader: ethers.utils.arrayify( + "0xf90215a058ee3c05e880cb25a3db92b9f1479c5453690ca97f9bcbb18d21965d3213578ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea674fdde714fd979de3edf0f56aa9716b898ec8a0a448355652812a7d518b5c979a15bba02cfe4576d8eb61e8b5731ecc37f2bec6a0049f25ed97f9ed9a9c8521ab39cd2c48438d1d18c84dcab5bf494c19595bd462a0b1169f28bdbe5dd61ebc20b7a459be9d7fa898f5a3ba5fed6d502d94b9a8101bb901001000008180000210000080010001080310e004800c3040000060484000010804088050044302a500240041040010012120840002400005092000808000640012081000880010008040200208000004050800400002244044006041040040010890040504020040008004222502000800220000021800006400802036500000000400014640d00020002110000001440000001509543802080004210004100de04744a2810000000032250080810000502210c04289480800000423080800004000a020220030203000020001000000042c00420090000008003308459020e010a01000200190900040e81000040040000020000a8044001000202010000600c087086c49cadb1b57839898538398909483984b9e845eb02fbf94505059452d65746865726d696e652d6575312d34a06d0287c21536fac432714bd3f3712ff1a7e409faf1b10edac9b9547da1d4f7b188930531280477460c", + ), + hash: "0x4a65bcdf3466a16740b74849cc10fc57d4acb24cce148665482812699a400464", + }, + { + num: 10000468, + rlpHeader: ethers.utils.arrayify( + "0xf9020da04a65bcdf3466a16740b74849cc10fc57d4acb24cce148665482812699a400464a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479404668ec2f57cc15c381b461b9fedab5d451c8f7fa0bcd4ddbb7125a5c06df96d862921dc0bba8664b3f759a233fe565a615c0ab3eaa0087ab379852c83e4b660de1668fc93333201ad0d233167ea6cef8bacaf5cba2aa0d81855037b2a6b56eba0c2ed129fb4102fb831b0baa187a0f6e0c155400f7855b9010080040040200000000010102081000000500040010408040800010110000000008000005808020000902021818000210000000000081100401000400014400001041008000020448800180128800008000200000420e01200000000000000011000001000020000208000b42200a0008000510200080200008c002018108010014030200000080000000002000010008000011008004003081000400080100803040080040300000002044080480000000000008080101000000050000000000840000002200040000a0080000442008006005502800000040008000890201002022402208002900020900000000080000100100201080000000003400000004887086d57541477ba839898548398968083989147845eb02fc28c73706964657230380b03ac53a076c676a0ab090b373b6242851a4beab7b8cdc9d3ebe211747a255b78c0278c42880ea13d40042dd1e6", + ), + hash: "0x00fd2589a272b85ffaf63223641571bf95891c936b7514ee4e87a593e52de7c9", + }, + { + num: 10000469, + rlpHeader: ethers.utils.arrayify( + "0xf90211a000fd2589a272b85ffaf63223641571bf95891c936b7514ee4e87a593e52de7c9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945a0b54d5dc17e0aadc383d2db43b0a0d3e029c4ca01b28d3b4e4d3442a9e6caed9f80b6a639bce6f3a283d4e004e6bb44e483ceeeba067c00d9067bc023b8fab8e3afd1bc0f2470f08003bdf9f167fbfeede2422ac4ea09d8b344d9ab1b7288f8c5e644c53b1a5288da6d6ee0e388ec76f291def48da15b90100c462095870a26a0804132e208110329710d459054558159c103208d44820002016108136199200061063699d8400254a22828c11b5512e3303c98ec7747cc02d00161880c2f2c580e806bccc04805190265f096601342058020a8324c277735d8202220412f03303201252a3000038883a4bb0010e6b004408306232150a84d110100d0c4b9d228022812602c05c801d20500d4ed10010ce2400428a96950a98050c00e603292a806c4983b25814880000440a23821191121996410c5110c949616c2066a4a0488087d4c226c14208042c00d609b5cc44051400219d93626818728612a9b18690e03c902014a900e0018828011494b80d4708799b0d8a83cace87086e64fefefb48839898558398968083986664845eb02fc7906574682d70726f2d687a662d74303032a09f1918a362b55ebd072cc9548fb74f89301d41c2a1feb13c08a1c2c3cb0606d88810dfa530069367fb", + ), + hash: "0x325fde74e261fc483a16506bbc711b645b043ad24c7db0136845a1be262cf0c9", + }, + { + num: 10000470, + rlpHeader: ethers.utils.arrayify( + "0xf90215a0325fde74e261fc483a16506bbc711b645b043ad24c7db0136845a1be262cf0c9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea674fdde714fd979de3edf0f56aa9716b898ec8a020647cfa35563093442a12d80bf2bacb83da1de8340366677f3822591a334ccea066ad7285f6c5b6407f62c6b65a83aeaaa71ad9a97c2bb15139140f2dbb60f7e0a0c0e633851d0b5ce661ecc054517425e82425fcc6170db9693e5b5a6dd5ef6d6bb90100c0c000c1520708182080c8e461891c2402800a80d44a00034259414012012a5006a1416331181504902044960808f1129018800311621e920886804693749b10542400142e984580ccba634881c4156962200ecfb004000005468db44842781c59923110262660802315006106388b028412c42c000820c508e66b7851fa68002008144cd7860cd884280802915163399c168d5a11b0649486084110149469a1e61c31134204b903206566885180bc0426c0c6c0a4d408e182242f08180d204c624a040248425041ac028010d088820402ba4bd38c2d1215829300543465603822110500811290490148049300040e000c280086a09e8100089818ce480a887e87086c4965bf3c8a839898568398705c839847d2845eb02fe994505059452d65746865726d696e652d6575312d35a09d8ae288d0eede524f3ef5e6cfcc5ba07f380bc695bb71578a7b91cfa517071b8859d0976006378e52", + ), + hash: "0x5cf096dfd1fc2d2947a96fdec5377ab7beaa0eb00c80728a3b96f0864cec506a", + }, +]; + +const maticBlocks: TestBlocks[] = [ + { + num: 10000467, + rlpHeader: ethers.utils.arrayify( + "0xf9025da0212093b89337e6741aca0c6c1cbfc64b56155bdcc3623fa9bcbfa0498fa135aba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0ac0ec242516093308f7a2cc6965f38835eb3db69cba93401daef672666a3aefea0d06985e9ae671d22fb4b3d73ef4e074a448f66b4769ad8d59c3b8aef1ede15e2a00076d4897a88e08c25ca12c558f622d03d532d8b674e8c6b9847000b98dbe480b90100040000000200000000000000000000000000000000000000100000000000400000000000000000000000002800000000100080000000000000000001000000400000000000000000000000080002008000000000000000000021000000000000000000000200000000001000000008000000000008000001800800100000000000010000000010100000800000000000001000000200100000000000000000002000000004000000000000000080010000000000000000200000000000000040000000420000000000010000000000000004040004000000001000001000200100100080000000000400000000100000100000000000000000000000021000000e839898538401312d008302e54b84600df884b861d78301091883626f7288676f312e31352e35856c696e7578000000000000000003eb49c29f5facd767206f64b8a5c9b325bced5c9156f489c6281c68eddc9e5f2ef1177c02a99d8ab6216dcf2879eefddfc27c75ffa9ef6a2185ce9983d1434901a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0x6c3b869ca26fece236545f7914d8249651d729852dc1445f53a94d5a59cdc9da", + }, + { + num: 10000468, + rlpHeader: ethers.utils.arrayify( + "0xf9025da06c3b869ca26fece236545f7914d8249651d729852dc1445f53a94d5a59cdc9daa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fa236c78bbe5939cc62985e32582c2158468a5b2b4dd02d514edb0bea95f0fd3a0e05ccfb09764e5cd6811ef2c2616d4a57f187be84235e2569c9b8d70489f1a44a0aea27aed2ad1d553e30501e6fe47fee0842c3b7ce5867e579b29975f02ec4282b90100008000100000000000000400080800000009000000010020000000000800000000000000080000000000000000000000000080000080000820400000000000000000200000000000000000080000008000200000200000000003009000020000000200000010000000001000000000000000000000000000800040100000000000000000000010000000100100000000000000000102004000000040000000002000000008000000000000000000000000000000200000000000000000000041000000020000080001010000000000000008000000110000001001800020000000100000000001400000040000000000000010010000000001000000001000000e839898548401312d00830494ed84600df886b861d78301091883626f7288676f312e31352e35856c696e75780000000000000000aa8ed86143b48b6aa7170d2083c3a7be31cbdfdc40f39badb8747f4c2198279a71c0d3eb5d25f3b7da5a48b887f61e22fe0baa692aa03807ad12f6fe25af087e00a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0x258aa48bde013579fbfef2e222bcc222b1f57bf898a71c623f9024229c9f6111", + }, + { + num: 10000469, + rlpHeader: ethers.utils.arrayify( + "0xf9025aa0258aa48bde013579fbfef2e222bcc222b1f57bf898a71c623f9024229c9f6111a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fa236c78bbe5939cc62985e32582c2158468a5b2b4dd02d514edb0bea95f0fd3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e839898558401312d008084600df888b861d78301091883626f7288676f312e31352e35856c696e75780000000000000000bd8668cc5d89583a7cc26fb96650e61f045ffe5248ae80c667ba7648df41e3d552060998ac151f2d15bd1b98f0a2a50c4281729a4c0aae4758a3bad280207c2901a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0x611779767f1deb5a17723ec71d1b397b18a0fc9a40d282810a33bd6a0a5f46f9", + }, + { + num: 10000470, + rlpHeader: ethers.utils.arrayify( + "0xf9025aa0611779767f1deb5a17723ec71d1b397b18a0fc9a40d282810a33bd6a0a5f46f9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fa236c78bbe5939cc62985e32582c2158468a5b2b4dd02d514edb0bea95f0fd3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e839898568401312d008084600df88ab861d78301091883626f7288676f312e31352e35856c696e75780000000000000000b617675c3b01e98319508130e1a583d57ce6b3a8a97fa2fbdaa33673cc6c609d6f7c361c833838f54b724d3a83cdd73e2398bb147970cd0b057865386cb08e1300a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0x2edf2f5c5faa5046b2304f76c92096a25e7c4343a7b75c36b29e8e9755d93397", + }, +]; + +// The following headers from Binance Smart Chain were retrieved using `go run +// binance.go`, where binance.go contains +// +// package main +// +// import ( +// "context" +// "fmt" +// "log" +// "math/big" +// "math/rand" +// "strings" +// +// "github.com/ethereum/go-ethereum/ethclient" +// "github.com/ethereum/go-ethereum/rlp" +// ) +// +// var tsBlockTemplate = ` +// { +// num: %d, +// rlpHeader: ethers.utils.arrayify( +// '0x%x', +// ), +// hash: '0x%x', +// }, +// ` +// +// func main() { +// client, err := ethclient.Dial("https://bsc-dataseed.binance.org/") +// if err != nil { +// log.Fatal(err) +// } +// +// header, err := client.HeaderByNumber(context.Background(), nil) +// if err != nil { +// log.Fatal(err) +// } +// topBlockNum := header.Number.Int64() +// numBlocks := int64(4) +// if topBlockNum < numBlocks { +// log.Fatalf("need at least %d consecutive blocks", numBlocks) +// } +// targetBlock := int64(rand.Intn(int(topBlockNum - numBlocks))) +// simulatedHeadBlock := targetBlock + numBlocks - 1 +// for blockNum := targetBlock; blockNum <= simulatedHeadBlock; blockNum++ { +// header, err := client.HeaderByNumber(context.Background(), big.NewInt(blockNum)) +// if err != nil { +// log.Fatal(err) +// } +// s, err := rlp.EncodeToBytes(header) +// if err != nil { +// log.Fatalf("could not encode header: got error %s from %v", err, header) +// } +// // fmt.Printf("header for block number %d: 0x%x\n", blockNum, s) +// fmt.Printf(strings.TrimLeft(tsBlockTemplate, "\n"), blockNum, s, header.Hash()) +// } +// } +const binanceBlocks: TestBlocks[] = [ + { + num: 1875651, + rlpHeader: ethers.utils.arrayify( + "0xf9025da029c26248bebbe0d0acb209d13ac9337c4b5c313696c031dd63b3cd16cbdc0c21a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794b8f7166496996a7da21cf1f1b04d9b3e26a3d077a03f962867b5e86191c3280bd52c4249587e08ddfa9851cea981fb7a5721c9157aa05924ae05d17347687ba81d093aee159ccc65cefc8314b0515ef921e553df05a2a089af99a7afa586e7d67062d051df4255304bb730f6d62fdd3bdb207f1513b23bb901000100000000000000000800000000000000000000000200000000000000800000000000000200100000000000000800000000000000000000000000000000000000000000000000800000140800000008201000001000000202000000001200000000002002020000000000000000080000000000000002000000001000000000000002000000008010000000000000000002040080008400280000c00000081000400000004000000010000000020000000000000000000000000000000000000001000210200000000000000000000800000000000000000000000000002010000004000000000001000000000000000000000800020000000000000000000002831c9ec38401c9c380830789c2845f9faab1b861d883010002846765746888676f312e31332e34856c696e7578000000000000003311ee6830f31dc9116d8a59178b539d91eb6811c1d533c4a59bf77262689c552218bb1eae9cb9d6bf6e1066bea78052c8767313ace71c919d02e70760bd255401a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0xe0a935b1e37420ac1d855215bdad4730a5ffe315eda287c6c18aa86c426ede74", + }, + { + num: 1875652, + rlpHeader: ethers.utils.arrayify( + "0xf9025da0e0a935b1e37420ac1d855215bdad4730a5ffe315eda287c6c18aa86c426ede74a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c2be4ec20253b8642161bc3f444f53679c1f3d47a0dbf2d40cf5533b65ac9b5be35cac75f7b106244ba14ee476a832c28d46a53452a04f83b8a51d3e17b6a02a0caa79acc7597996c5b8c68dba12c33095ae086089eea02fa2642645b2de17227a6c18c3fa491f54b3bdfe8ac8e04924a33a005a0e9e61b901000100000100000000000008000000000000000000040000000000000000800000000000000000000000000000000800000800000000000400000000000020000040100080000000000000000800000000209000001000000200000000801000400800002002030000000000000100080000002000000002004000011000000002000100040000000000100000000000000000040100009000300000000000000002004000004000000000000000020000002000000010000000200000800000000001000280000000000000008000000000000000800000000000020000002000041000000000000001200020001000080000002a40020040000000000000000002831c9ec48401c9c38083044b40845f9faab4b861d883010002846765746888676f312e31332e34856c696e757800000000000000cfc02687b2394922055792a8e67dad566f6690de06b229d752433b2067207b5f43b9f3c63f91cea5a79bbfc51d9132b933a706ab504038a92f37d57af2bb6c2e01a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0x629e5abcae42940e00d7b38aa7b2ecccfbab582cb7a0b2c3658c2dad8e66549d", + }, + { + num: 1875653, + rlpHeader: ethers.utils.arrayify( + "0xf9025da0629e5abcae42940e00d7b38aa7b2ecccfbab582cb7a0b2c3658c2dad8e66549da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ce2fd7544e0b2cc94692d4a704debef7bcb61328a0718e7db53041585a814d658c32c88fd550c2c6d200826020008925e0a0f7967fa000fbf842e492a47cc9786885783259a08aed71055e78216006408932515fd960a0c7ffeb2189b8fcde43733cf1958cdb1c38c44052cfbb41125382240c232a98f8b901000000000000000000000000000000000000000002000000000004000000000000000000010000000000000000000000000000000000000200000000004020200000010000000800000000208800000000201000000000000000080000000000000000002002220000000000000000080000000000000000000000001000000000100000000000080010000000000000000000040000000000000000000000000002000000000008000000004000000000000000000000200000000000000000000000000202000000000000000000000000000000000008000000000000002080001000000000000001000000000000000000080100000000000000000000000002831c9ec58401c9c38083025019845f9faab7b861d883010002846765746888676f312e31332e34856c696e7578000000000000008c3c7a5c83e930fbd9d14f83c9b3931f032f0f678919c35b8b32ca6dae9948950bfa326fae134fa234fa7b84c06bdc3f7c6d6414c2a266df1339e563be8bd9cc00a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0xae8574651adabfd0ca55e2cee0e2e639ced73ec1cc0a35debeeceee6943442a9", + }, + { + num: 1875654, + rlpHeader: ethers.utils.arrayify( + "0xf9025da0ae8574651adabfd0ca55e2cee0e2e639ced73ec1cc0a35debeeceee6943442a9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d6caa02bbebaebb5d7e581e4b66559e635f805ffa02df6a1173c63ec0a8acc46c818670f030aece1154b9f3bbc70f46a8427dd8dd6a0fa8835c499682d8c90759ff9ea1c291048755b967e48880a0fc21d19ec034a59a0b4e22607cb105c04156044b3f98c2cecae1553b45aa9b6044c37573791a27576b901000200000008000000000001000000000000000000000020000000000000020000000000000000000000000000000000000000000000000040000000000220000000000000000400000000001802000000201000000000000000000000000000000000002002020000000000000000080000000000000000000000001000000000000000000000100000000000000000000000040000000000000000010200200002000400000000400000000200000000000000080000000000000000000008000000000200000000000000000000000000000000000000000000000000002000001000000000000001000000000000000000000000000000000008080000000002831c9ec68401c9c3808301e575845f9faabab861d883010002846765746888676f312e31332e34856c696e757800000000000000399e73b0e963ec029e815623a414aa852508a28dd9799a1bf4e2380c8db687a46cc5b6cc20352ae21e35cfd28124a32fcd49ac8fac5b03901b3e03963e4fff5801a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + ), + hash: "0x189990455c59a5dea78071df9a2008ede292ff0a062fc5c4c6ca35fbe476f834", + }, +]; + +before(async () => { + personas = (await getUsers()).personas; + blockhashStoreTestHelperFactory = await ethers.getContractFactory("BlockhashStoreTestHelper", personas.Default); +}); + +runBlockhashStoreTests(mainnetBlocks, "Ethereum"); +runBlockhashStoreTests(maticBlocks, "Matic"); +runBlockhashStoreTests(binanceBlocks, "Binance Smart Chain"); + +async function runBlockhashStoreTests(blocks: TestBlocks[], description: string) { + describe(`BlockhashStore (${description})`, () => { + let blockhashStoreTestHelper: Contract; + + beforeEach(async () => { + blockhashStoreTestHelper = await blockhashStoreTestHelperFactory.connect(personas.Default).deploy(); + + const [lastBlock] = blocks.slice(-1); + await blockhashStoreTestHelper.connect(personas.Default).godmodeSetHash(lastBlock.num, lastBlock.hash); + assert.strictEqual(await blockhashStoreTestHelper.getBlockhash(lastBlock.num), lastBlock.hash); + }); + + it("getBlockhash reverts for unknown blockhashes", async () => { + await expect(blockhashStoreTestHelper.getBlockhash(99999999)).to.be.revertedWith("blockhash not found in store"); + }); + + it("storeVerifyHeader records valid blockhashes", async () => { + for (let i = blocks.length - 2; i >= 0; i--) { + assert.strictEqual( + ethers.utils.keccak256(blocks[i + 1].rlpHeader), + await blockhashStoreTestHelper.getBlockhash(blocks[i + 1].num), + ); + await blockhashStoreTestHelper + .connect(personas.Default) + .storeVerifyHeader(blocks[i].num, blocks[i + 1].rlpHeader); + assert.strictEqual(await blockhashStoreTestHelper.getBlockhash(blocks[i].num), blocks[i].hash); + } + }); + + it("storeVerifyHeader rejects unknown headers", async () => { + const unknownBlock = blocks[0]; + await expect( + blockhashStoreTestHelper + .connect(personas.Default) + .storeVerifyHeader(unknownBlock.num - 1, unknownBlock.rlpHeader), + ).to.be.revertedWith("header has unknown blockhash"); + }); + + it("storeVerifyHeader rejects corrupted headers", async () => { + const [lastBlock] = blocks.slice(-1); + const modifiedHeader = new Uint8Array(lastBlock.rlpHeader); + modifiedHeader[137] += 1; + await expect( + blockhashStoreTestHelper.connect(personas.Default).storeVerifyHeader(lastBlock.num - 1, modifiedHeader), + ).to.be.revertedWith("header has unknown blockhash"); + }); + + it("store accepts recent block numbers", async () => { + await ethers.provider.send("evm_mine", []); + + const n = (await ethers.provider.getBlockNumber()) - 1; + await blockhashStoreTestHelper.connect(personas.Default).store(n); + + assert.equal(await blockhashStoreTestHelper.getBlockhash(n), (await ethers.provider.getBlock(n)).hash); + }); + + it("store rejects future block numbers", async () => { + await expect(blockhashStoreTestHelper.connect(personas.Default).store(99999999999)).to.be.revertedWith( + "blockhash(n) failed", + ); + }); + + it("store rejects old block numbers", async () => { + for (let i = 0; i < 300; i++) { + await ethers.provider.send("evm_mine", []); + } + + await expect( + blockhashStoreTestHelper.connect(personas.Default).store((await ethers.provider.getBlockNumber()) - 256), + ).to.be.revertedWith("blockhash(n) failed"); + }); + + it("storeEarliest works", async () => { + for (let i = 0; i < 300; i++) { + await ethers.provider.send("evm_mine", []); + } + + await blockhashStoreTestHelper.connect(personas.Default).storeEarliest(); + + const n = (await ethers.provider.getBlockNumber()) - 256; + assert.equal(await blockhashStoreTestHelper.getBlockhash(n), (await ethers.provider.getBlock(n)).hash); + }); + }); +} diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/CheckedMath.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/CheckedMath.test.ts new file mode 100644 index 0000000..48aced3 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/CheckedMath.test.ts @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: MIT +// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/c9630526e24ba53d9647787588a19ffaa3dd65e1/test/math/SignedSafeMath.test.js + +import { ethers } from "hardhat"; +import { assert } from "chai"; +import { BigNumber, constants, Contract, ContractFactory } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { bigNumEquals } from "../test-helpers/matchers"; + +let mathFactory: ContractFactory; +let personas: Personas; + +before(async () => { + personas = (await getUsers()).personas; + mathFactory = await ethers.getContractFactory("CheckedMathTestHelper", personas.Default); +}); + +const int256Max = constants.MaxInt256; +const int256Min = constants.MinInt256; + +describe("CheckedMath", () => { + let math: Contract; + + beforeEach(async () => { + math = await mathFactory.connect(personas.Default).deploy(); + }); + + describe("#add", () => { + const a = BigNumber.from("1234"); + const b = BigNumber.from("5678"); + + it("is commutative", async () => { + const c1 = await math.add(a, b); + const c2 = await math.add(b, a); + + bigNumEquals(c1.result, c2.result); + assert.isTrue(c1.ok); + assert.isTrue(c2.ok); + }); + + it("is commutative with big numbers", async () => { + const c1 = await math.add(int256Max, int256Min); + const c2 = await math.add(int256Min, int256Max); + + bigNumEquals(c1.result, c2.result); + assert.isTrue(c1.ok); + assert.isTrue(c2.ok); + }); + + it("returns false when overflowing", async () => { + const c1 = await math.add(int256Max, 1); + const c2 = await math.add(1, int256Max); + + bigNumEquals(0, c1.result); + bigNumEquals(0, c2.result); + assert.isFalse(c1.ok); + assert.isFalse(c2.ok); + }); + + it("returns false when underflowing", async () => { + const c1 = await math.add(int256Min, -1); + const c2 = await math.add(-1, int256Min); + + bigNumEquals(0, c1.result); + bigNumEquals(0, c2.result); + assert.isFalse(c1.ok); + assert.isFalse(c2.ok); + }); + }); + + describe("#sub", () => { + const a = BigNumber.from("1234"); + const b = BigNumber.from("5678"); + + it("subtracts correctly if it does not overflow and the result is negative", async () => { + const c = await math.sub(a, b); + const expected = a.sub(b); + + bigNumEquals(expected, c.result); + assert.isTrue(c.ok); + }); + + it("subtracts correctly if it does not overflow and the result is positive", async () => { + const c = await math.sub(b, a); + const expected = b.sub(a); + + bigNumEquals(expected, c.result); + assert.isTrue(c.ok); + }); + + it("returns false on overflow", async () => { + const c = await math.sub(int256Max, -1); + + bigNumEquals(0, c.result); + assert.isFalse(c.ok); + }); + + it("returns false on underflow", async () => { + const c = await math.sub(int256Min, 1); + + bigNumEquals(0, c.result); + assert.isFalse(c.ok); + }); + }); + + describe("#mul", () => { + const a = BigNumber.from("5678"); + const b = BigNumber.from("-1234"); + + it("is commutative", async () => { + const c1 = await math.mul(a, b); + const c2 = await math.mul(b, a); + + bigNumEquals(c1.result, c2.result); + assert.isTrue(c1.ok); + assert.isTrue(c2.ok); + }); + + it("multiplies by 0 correctly", async () => { + const c = await math.mul(a, 0); + + bigNumEquals(0, c.result); + assert.isTrue(c.ok); + }); + + it("returns false on multiplication overflow", async () => { + const c = await math.mul(int256Max, 2); + + bigNumEquals(0, c.result); + assert.isFalse(c.ok); + }); + + it("returns false when the integer minimum is negated", async () => { + const c = await math.mul(int256Min, -1); + + bigNumEquals(0, c.result); + assert.isFalse(c.ok); + }); + }); + + describe("#div", () => { + const a = BigNumber.from("5678"); + const b = BigNumber.from("-5678"); + + it("divides correctly", async () => { + const c = await math.div(a, b); + + bigNumEquals(a.div(b), c.result); + assert.isTrue(c.ok); + }); + + it("divides a 0 numerator correctly", async () => { + const c = await math.div(0, a); + + bigNumEquals(0, c.result); + assert.isTrue(c.ok); + }); + + it("returns complete number result on non-even division", async () => { + const c = await math.div(7000, 5678); + + bigNumEquals(1, c.result); + assert.isTrue(c.ok); + }); + + it("reverts when 0 is the denominator", async () => { + const c = await math.div(a, 0); + + bigNumEquals(0, c.result); + assert.isFalse(c.ok); + }); + + it("reverts on underflow with a negative denominator", async () => { + const c = await math.div(int256Min, -1); + + bigNumEquals(0, c.result); + assert.isFalse(c.ok); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/DeviationFlaggingValidator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/DeviationFlaggingValidator.test.ts new file mode 100644 index 0000000..0c6d01b --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/DeviationFlaggingValidator.test.ts @@ -0,0 +1,240 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { BigNumber, Contract, ContractFactory } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { bigNumEquals } from "../test-helpers/matchers"; + +let personas: Personas; +let validatorFactory: ContractFactory; +let flagsFactory: ContractFactory; +let acFactory: ContractFactory; + +before(async () => { + personas = (await getUsers()).personas; + validatorFactory = await ethers.getContractFactory("DeviationFlaggingValidator", personas.Carol); + flagsFactory = await ethers.getContractFactory("Flags", personas.Carol); + acFactory = await ethers.getContractFactory("SimpleWriteAccessController", personas.Carol); +}); + +describe("DeviationFlaggingValidator", () => { + let validator: Contract; + let flags: Contract; + let ac: Contract; + const flaggingThreshold = 10000; // 10% + const previousRoundId = 2; + const previousValue = 1000000; + const currentRoundId = 3; + const currentValue = 1000000; + + beforeEach(async () => { + ac = await acFactory.connect(personas.Carol).deploy(); + flags = await flagsFactory.connect(personas.Carol).deploy(ac.address); + validator = await validatorFactory.connect(personas.Carol).deploy(flags.address, flaggingThreshold); + await ac.connect(personas.Carol).addAccess(validator.address); + }); + + it("has a limited public interface", () => { + publicAbi(validator, [ + "THRESHOLD_MULTIPLIER", + "flaggingThreshold", + "flags", + "isValid", + "setFlagsAddress", + "setFlaggingThreshold", + "validate", + // Owned methods: + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#constructor", () => { + it("sets the arguments passed in", async () => { + assert.equal(flags.address, await validator.flags()); + bigNumEquals(flaggingThreshold, await validator.flaggingThreshold()); + }); + }); + + describe("#validate", () => { + describe("when the deviation is greater than the threshold", () => { + const currentValue = 1100010; + + it("does raises a flag for the calling address", async () => { + await expect( + validator.connect(personas.Nelly).validate(previousRoundId, previousValue, currentRoundId, currentValue), + ) + .to.emit(flags, "FlagRaised") + .withArgs(await personas.Nelly.getAddress()); + }); + + it("uses less than the gas allotted by the aggregator", async () => { + const tx = await validator + .connect(personas.Nelly) + .validate(previousRoundId, previousValue, currentRoundId, currentValue); + const receipt = await tx.wait(); + assert(receipt); + if (receipt && receipt.gasUsed) { + assert.isAbove(receipt.gasUsed.toNumber(), 60000); + } + }); + }); + + describe("when the deviation is less than or equal to the threshold", () => { + const currentValue = 1100009; + + it("does raises a flag for the calling address", async () => { + await expect( + validator.connect(personas.Nelly).validate(previousRoundId, previousValue, currentRoundId, currentValue), + ).to.not.emit(flags, "FlagRaised"); + }); + + it("uses less than the gas allotted by the aggregator", async () => { + const tx = await validator + .connect(personas.Nelly) + .validate(previousRoundId, previousValue, currentRoundId, currentValue); + const receipt = await tx.wait(); + assert(receipt); + if (receipt && receipt.gasUsed) { + assert.isAbove(receipt.gasUsed.toNumber(), 24000); + } + }); + }); + + describe("when called with a previous value of zero", () => { + const previousValue = 0; + + it("does not raise any flags", async () => { + const tx = await validator + .connect(personas.Nelly) + .validate(previousRoundId, previousValue, currentRoundId, currentValue); + const receipt = await tx.wait(); + assert.equal(0, receipt.events?.length); + }); + }); + }); + + describe("#isValid", () => { + const previousValue = 1000000; + + describe("with a validation larger than the deviation", () => { + const currentValue = 1100010; + it("is not valid", async () => { + assert.isFalse(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + + describe("with a validation smaller than the deviation", () => { + const currentValue = 1100009; + it("is valid", async () => { + assert.isTrue(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + + describe("with positive previous and negative current", () => { + const previousValue = 1000000; + const currentValue = -900000; + it("correctly detects the difference", async () => { + assert.isFalse(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + + describe("with negative previous and positive current", () => { + const previousValue = -900000; + const currentValue = 1000000; + it("correctly detects the difference", async () => { + assert.isFalse(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + + describe("when the difference overflows", () => { + const previousValue = BigNumber.from(2).pow(255).sub(1); + const currentValue = BigNumber.from(-1); + + it("does not revert and returns false", async () => { + assert.isFalse(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + + describe("when the rounding overflows", () => { + const previousValue = BigNumber.from(2).pow(255).div(10000); + const currentValue = BigNumber.from(1); + + it("does not revert and returns false", async () => { + assert.isFalse(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + + describe("when the division overflows", () => { + const previousValue = BigNumber.from(2).pow(255).sub(1); + const currentValue = BigNumber.from(-1); + + it("does not revert and returns false", async () => { + assert.isFalse(await validator.isValid(0, previousValue, 1, currentValue)); + }); + }); + }); + + describe("#setFlaggingThreshold", () => { + const newThreshold = 777; + + it("changes the flagging thresold", async () => { + assert.equal(flaggingThreshold, await validator.flaggingThreshold()); + + await validator.connect(personas.Carol).setFlaggingThreshold(newThreshold); + + assert.equal(newThreshold, await validator.flaggingThreshold()); + }); + + it("emits a log event only when actually changed", async () => { + await expect(validator.connect(personas.Carol).setFlaggingThreshold(newThreshold)) + .to.emit(validator, "FlaggingThresholdUpdated") + .withArgs(flaggingThreshold, newThreshold); + + await expect(validator.connect(personas.Carol).setFlaggingThreshold(newThreshold)).to.not.emit( + validator, + "FlaggingThresholdUpdated", + ); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(validator.connect(personas.Neil).setFlaggingThreshold(newThreshold)).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + }); + + describe("#setFlagsAddress", () => { + const newFlagsAddress = "0x0123456789012345678901234567890123456789"; + + it("changes the flags address", async () => { + assert.equal(flags.address, await validator.flags()); + + await validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress); + + assert.equal(newFlagsAddress, await validator.flags()); + }); + + it("emits a log event only when actually changed", async () => { + await expect(validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress)) + .to.emit(validator, "FlagsAddressUpdated") + .withArgs(flags.address, newFlagsAddress); + + await expect(validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress)).to.not.emit( + validator, + "FlagsAddressUpdated", + ); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(validator.connect(personas.Neil).setFlagsAddress(newFlagsAddress)).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/EACAggregatorProxy.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/EACAggregatorProxy.test.ts new file mode 100644 index 0000000..2f09787 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/EACAggregatorProxy.test.ts @@ -0,0 +1,271 @@ +import { + contract, + helpers as h, + matchers, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { EACAggregatorProxy__factory } from '../../ethers/v0.6/factories/EACAggregatorProxy__factory' +import { SimpleReadAccessController__factory } from '../../ethers/v0.6/factories/SimpleReadAccessController__factory' +import { MockV3Aggregator__factory } from '../../ethers/v0.6/factories/MockV3Aggregator__factory' +import { FluxAggregatorTestHelper__factory } from '../../ethers/v0.6/factories/FluxAggregatorTestHelper__factory' + +let personas: setup.Personas +let defaultAccount: ethers.Wallet + +const provider = setup.provider() +const linkTokenFactory = new contract.LinkToken__factory() +const accessControlFactory = new SimpleReadAccessController__factory() +const aggregatorFactory = new MockV3Aggregator__factory() +const testHelperFactory = new FluxAggregatorTestHelper__factory() +const proxyFactory = new EACAggregatorProxy__factory() +const emptyAddress = '0x0000000000000000000000000000000000000000' + +beforeAll(async () => { + const users = await setup.users(provider) + + personas = users.personas + defaultAccount = users.roles.defaultAccount +}) + +describe('EACAggregatorProxy', () => { + const deposit = h.toWei('100') + const answer = h.numToBytes32(54321) + const answer2 = h.numToBytes32(54320) + const roundId = 17 + const decimals = 18 + const timestamp = 678 + const startedAt = 677 + + let link: contract.Instance + let controller: contract.Instance + let aggregator: contract.Instance + let aggregator2: contract.Instance + let proxy: contract.Instance + let testHelper: contract.Instance + const phaseBase = h.bigNum(2).pow(64) + + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(defaultAccount).deploy() + aggregator = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, 0) + controller = await accessControlFactory.connect(defaultAccount).deploy() + await aggregator.updateRoundData(roundId, answer, timestamp, startedAt) + await link.transfer(aggregator.address, deposit) + proxy = await proxyFactory + .connect(defaultAccount) + .deploy(aggregator.address, controller.address) + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(proxyFactory, [ + 'accessController', + 'aggregator', + 'confirmAggregator', + 'decimals', + 'description', + 'getAnswer', + 'getRoundData', + 'getTimestamp', + 'latestAnswer', + 'latestRound', + 'latestRoundData', + 'latestTimestamp', + 'phaseAggregators', + 'phaseId', + 'proposeAggregator', + 'proposedAggregator', + 'proposedGetRoundData', + 'proposedLatestRoundData', + 'setController', + 'version', + // Ownable methods: + 'acceptOwnership', + 'owner', + 'transferOwnership', + ]) + }) + + describe('callers can call view functions without explicit access', () => { + it('#latestAnswer', async () => { + await proxy.connect(personas.Carol).latestAnswer() + }) + + it('#latestTimestamp', async () => { + await proxy.connect(personas.Carol).latestTimestamp() + }) + + it('#getAnswer', async () => { + await proxy.connect(personas.Carol).getAnswer(phaseBase.add(1)) + }) + + it('#getTimestamp', async () => { + await proxy.connect(personas.Carol).getTimestamp(phaseBase.add(1)) + }) + + it('#latestRound', async () => { + await proxy.connect(personas.Carol).latestRound() + }) + + it('#getRoundData', async () => { + await proxy.connect(personas.Carol).getRoundData(phaseBase.add(1)) + }) + + it('#proposedGetRoundData', async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, answer2) + await proxy.proposeAggregator(aggregator2.address) + const latestRound = await aggregator2.latestRound() + await proxy.connect(personas.Carol).proposedGetRoundData(latestRound) + }) + + it('#proposedLatestRoundData', async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, answer2) + await proxy.proposeAggregator(aggregator2.address) + await proxy.connect(personas.Carol).proposedLatestRoundData() + }) + }) + + describe('if the caller is granted access', () => { + beforeEach(async () => { + await controller.addAccess(defaultAccount.address) + + matchers.bigNum( + ethers.utils.bigNumberify(answer), + await aggregator.latestAnswer(), + ) + const height = await aggregator.latestTimestamp() + assert.notEqual('0', height.toString()) + }) + + it('pulls the rate from the aggregator', async () => { + matchers.bigNum(answer, await proxy.latestAnswer()) + const latestRound = await proxy.latestRound() + matchers.bigNum(answer, await proxy.getAnswer(latestRound)) + }) + + it('pulls the timestamp from the aggregator', async () => { + matchers.bigNum( + await aggregator.latestTimestamp(), + await proxy.latestTimestamp(), + ) + const latestRound = await proxy.latestRound() + matchers.bigNum( + await aggregator.latestTimestamp(), + await proxy.getTimestamp(latestRound), + ) + }) + + it('getRoundData works', async () => { + const proxyRoundId = await proxy.latestRound() + const round = await proxy.getRoundData(proxyRoundId) + matchers.bigNum(proxyRoundId, round.roundId) + matchers.bigNum(answer, round.answer) + matchers.bigNum(startedAt, round.startedAt) + matchers.bigNum(timestamp, round.updatedAt) + }) + + describe('and an aggregator has been proposed', () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory + .connect(defaultAccount) + .deploy(decimals, answer2) + await proxy.proposeAggregator(aggregator2.address) + }) + + it('proposedGetRoundData works', async () => { + const latestRound = await aggregator2.latestRound() + const round = await proxy.proposedGetRoundData(latestRound) + matchers.bigNum(latestRound, round.roundId) + matchers.bigNum(answer2, round.answer) + }) + + it('proposedLatestRoundData works', async () => { + const latestRound = await aggregator2.latestRound() + const round = await proxy.proposedLatestRoundData() + matchers.bigNum(latestRound, round.roundId) + matchers.bigNum(answer2, round.answer) + }) + }) + + describe('without a proposed aggregator', () => { + it('proposedGetRoundData reverts', async () => { + await matchers.evmRevert(async () => { + await proxy.proposedGetRoundData(1) + }, 'No proposed aggregator present') + }) + + it('proposedLatestRoundData reverts', async () => { + await matchers.evmRevert(async () => { + await proxy.proposedLatestRoundData() + }, 'No proposed aggregator present') + }) + }) + + describe('when read from a contract that is not permissioned', () => { + beforeEach(async () => { + testHelper = await testHelperFactory.connect(personas.Carol).deploy() + }) + + it('does not allow reading', async () => { + await matchers.evmRevert( + testHelper.readLatestRoundData(proxy.address), + 'No access', + ) + }) + }) + }) + + describe('#setController', () => { + let newController: contract.Instance + + beforeEach(async () => { + newController = await accessControlFactory + .connect(defaultAccount) + .deploy() + }) + + describe('when called by a stranger', () => { + it('reverts', async () => { + await matchers.evmRevert(async () => { + await proxy + .connect(personas.Carol) + .setController(newController.address) + }, 'Only callable by owner') + }) + }) + + describe('when called by the owner', () => { + it('updates the controller contract', async () => { + await proxy.connect(defaultAccount).setController(newController.address) + assert.equal(await proxy.accessController(), newController.address) + }) + }) + + describe('when set to the zero address', () => { + beforeEach(async () => { + testHelper = await testHelperFactory.connect(personas.Carol).deploy() + }) + + it('allows anyone to read', async () => { + await matchers.evmRevert( + testHelper.readLatestRoundData(proxy.address), + 'No access', + ) + + await proxy.connect(defaultAccount).setController(emptyAddress) + + await testHelper.readLatestRoundData(proxy.address) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/Flags.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/Flags.test.ts new file mode 100644 index 0000000..b6678ea --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/Flags.test.ts @@ -0,0 +1,347 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Contract, ContractFactory } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; + +let personas: Personas; + +let controllerFactory: ContractFactory; +let flagsFactory: ContractFactory; +let consumerFactory: ContractFactory; + +let controller: Contract; +let flags: Contract; +let consumer: Contract; + +before(async () => { + personas = (await getUsers()).personas; + controllerFactory = await ethers.getContractFactory("SimpleWriteAccessController", personas.Nelly); + consumerFactory = await ethers.getContractFactory("FlagsTestHelper", personas.Nelly); + flagsFactory = await ethers.getContractFactory("Flags", personas.Nelly); +}); + +describe("Flags", () => { + beforeEach(async () => { + controller = await controllerFactory.deploy(); + flags = await flagsFactory.deploy(controller.address); + await flags.disableAccessCheck(); + consumer = await consumerFactory.deploy(flags.address); + }); + + it("has a limited public interface", async () => { + publicAbi(flags, [ + "getFlag", + "getFlags", + "lowerFlags", + "raiseFlag", + "raiseFlags", + "raisingAccessController", + "setRaisingAccessController", + // Ownable methods: + "acceptOwnership", + "owner", + "transferOwnership", + // AccessControl methods: + "addAccess", + "disableAccessCheck", + "enableAccessCheck", + "removeAccess", + "checkEnabled", + "hasAccess", + ]); + }); + + describe("#raiseFlag", () => { + describe("when called by the owner", () => { + it("updates the warning flag", async () => { + assert.equal(false, await flags.getFlag(consumer.address)); + + await flags.connect(personas.Nelly).raiseFlag(consumer.address); + + assert.equal(true, await flags.getFlag(consumer.address)); + }); + + it("emits an event log", async () => { + await expect(flags.connect(personas.Nelly).raiseFlag(consumer.address)) + .to.emit(flags, "FlagRaised") + .withArgs(consumer.address); + }); + + describe("if a flag has already been raised", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).raiseFlag(consumer.address); + }); + + it("emits an event log", async () => { + const tx = await flags.connect(personas.Nelly).raiseFlag(consumer.address); + const receipt = await tx.wait(); + assert.equal(0, receipt.events?.length); + }); + }); + }); + + describe("when called by an enabled setter", () => { + beforeEach(async () => { + await controller.connect(personas.Nelly).addAccess(await personas.Neil.getAddress()); + }); + + it("sets the flags", async () => { + await flags.connect(personas.Neil).raiseFlag(consumer.address), + assert.equal(true, await flags.getFlag(consumer.address)); + }); + }); + + describe("when called by a non-enabled setter", () => { + it("reverts", async () => { + await expect(flags.connect(personas.Neil).raiseFlag(consumer.address)).to.be.revertedWith( + "Not allowed to raise flags", + ); + }); + }); + + describe("when called when there is no raisingAccessController", () => { + beforeEach(async () => { + await expect( + flags.connect(personas.Nelly).setRaisingAccessController("0x0000000000000000000000000000000000000000"), + ).to.emit(flags, "RaisingAccessControllerUpdated"); + assert.equal("0x0000000000000000000000000000000000000000", await flags.raisingAccessController()); + }); + + it("succeeds for the owner", async () => { + await flags.connect(personas.Nelly).raiseFlag(consumer.address); + assert.equal(true, await flags.getFlag(consumer.address)); + }); + + it("reverts for non-owner", async () => { + await expect(flags.connect(personas.Neil).raiseFlag(consumer.address)).to.be.reverted; + }); + }); + }); + + describe("#raiseFlags", () => { + describe("when called by the owner", () => { + it("updates the warning flag", async () => { + assert.equal(false, await flags.getFlag(consumer.address)); + + await flags.connect(personas.Nelly).raiseFlags([consumer.address]); + + assert.equal(true, await flags.getFlag(consumer.address)); + }); + + it("emits an event log", async () => { + await expect(flags.connect(personas.Nelly).raiseFlags([consumer.address])) + .to.emit(flags, "FlagRaised") + .withArgs(consumer.address); + }); + + describe("if a flag has already been raised", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).raiseFlags([consumer.address]); + }); + + it("emits an event log", async () => { + const tx = await flags.connect(personas.Nelly).raiseFlags([consumer.address]); + const receipt = await tx.wait(); + assert.equal(0, receipt.events?.length); + }); + }); + }); + + describe("when called by an enabled setter", () => { + beforeEach(async () => { + await controller.connect(personas.Nelly).addAccess(await personas.Neil.getAddress()); + }); + + it("sets the flags", async () => { + await flags.connect(personas.Neil).raiseFlags([consumer.address]), + assert.equal(true, await flags.getFlag(consumer.address)); + }); + }); + + describe("when called by a non-enabled setter", () => { + it("reverts", async () => { + await expect(flags.connect(personas.Neil).raiseFlags([consumer.address])).to.be.revertedWith( + "Not allowed to raise flags", + ); + }); + }); + + describe("when called when there is no raisingAccessController", () => { + beforeEach(async () => { + await expect( + flags.connect(personas.Nelly).setRaisingAccessController("0x0000000000000000000000000000000000000000"), + ).to.emit(flags, "RaisingAccessControllerUpdated"); + + assert.equal("0x0000000000000000000000000000000000000000", await flags.raisingAccessController()); + }); + + it("succeeds for the owner", async () => { + await flags.connect(personas.Nelly).raiseFlags([consumer.address]); + assert.equal(true, await flags.getFlag(consumer.address)); + }); + + it("reverts for non-owners", async () => { + await expect(flags.connect(personas.Neil).raiseFlags([consumer.address])).to.be.reverted; + }); + }); + }); + + describe("#lowerFlags", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).raiseFlags([consumer.address]); + }); + + describe("when called by the owner", () => { + it("updates the warning flag", async () => { + assert.equal(true, await flags.getFlag(consumer.address)); + + await flags.connect(personas.Nelly).lowerFlags([consumer.address]); + + assert.equal(false, await flags.getFlag(consumer.address)); + }); + + it("emits an event log", async () => { + await expect(flags.connect(personas.Nelly).lowerFlags([consumer.address])) + .to.emit(flags, "FlagLowered") + .withArgs(consumer.address); + }); + + describe("if a flag has already been raised", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).lowerFlags([consumer.address]); + }); + + it("emits an event log", async () => { + const tx = await flags.connect(personas.Nelly).lowerFlags([consumer.address]); + const receipt = await tx.wait(); + assert.equal(0, receipt.events?.length); + }); + }); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(flags.connect(personas.Neil).lowerFlags([consumer.address])).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + }); + + describe("#getFlag", () => { + describe("if the access control is turned on", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).enableAccessCheck(); + }); + + it("reverts", async () => { + await expect(consumer.getFlag(consumer.address)).to.be.revertedWith("No access"); + }); + + describe("if access is granted to the address", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).addAccess(consumer.address); + }); + + it("does not revert", async () => { + await consumer.getFlag(consumer.address); + }); + }); + }); + + describe("if the access control is turned off", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).disableAccessCheck(); + }); + + it("does not revert", async () => { + await consumer.getFlag(consumer.address); + }); + + describe("if access is granted to the address", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).addAccess(consumer.address); + }); + + it("does not revert", async () => { + await consumer.getFlag(consumer.address); + }); + }); + }); + }); + + describe("#getFlags", () => { + beforeEach(async () => { + await flags.connect(personas.Nelly).disableAccessCheck(); + await flags + .connect(personas.Nelly) + .raiseFlags([await personas.Neil.getAddress(), await personas.Norbert.getAddress()]); + }); + + it("respects the access controls of #getFlag", async () => { + await flags.connect(personas.Nelly).enableAccessCheck(); + + await expect(consumer.getFlag(consumer.address)).to.be.revertedWith("No access"); + + await flags.connect(personas.Nelly).addAccess(consumer.address); + + await consumer.getFlag(consumer.address); + }); + + it("returns the flags in the order they are requested", async () => { + const response = await consumer.getFlags([ + await personas.Nelly.getAddress(), + await personas.Neil.getAddress(), + await personas.Ned.getAddress(), + await personas.Norbert.getAddress(), + ]); + + assert.deepEqual([false, true, false, true], response); + }); + }); + + describe("#setRaisingAccessController", () => { + let controller2: Contract; + + beforeEach(async () => { + controller2 = await controllerFactory.connect(personas.Nelly).deploy(); + await controller2.connect(personas.Nelly).enableAccessCheck(); + }); + + it("updates access control rules", async () => { + const neilAddress = await personas.Neil.getAddress(); + await controller.connect(personas.Nelly).addAccess(neilAddress); + await flags.connect(personas.Neil).raiseFlags([consumer.address]); // doesn't raise + + await flags.connect(personas.Nelly).setRaisingAccessController(controller2.address); + + await expect(flags.connect(personas.Neil).raiseFlags([consumer.address])).to.be.revertedWith( + "Not allowed to raise flags", + ); + }); + + it("emits a log announcing the change", async () => { + await expect(flags.connect(personas.Nelly).setRaisingAccessController(controller2.address)) + .to.emit(flags, "RaisingAccessControllerUpdated") + .withArgs(controller.address, controller2.address); + }); + + it("does not emit a log when there is no change", async () => { + await flags.connect(personas.Nelly).setRaisingAccessController(controller2.address); + + await expect(flags.connect(personas.Nelly).setRaisingAccessController(controller2.address)).to.not.emit( + flags, + "RaisingAccessControllerUpdated", + ); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(flags.connect(personas.Neil).setRaisingAccessController(controller2.address)).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/FluxAggregator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/FluxAggregator.test.ts new file mode 100644 index 0000000..a0ef5d2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/FluxAggregator.test.ts @@ -0,0 +1,2520 @@ +import { ethers } from "hardhat"; +import { assert, expect } from "chai"; +import { Signer, Contract, ContractFactory, BigNumber, BigNumberish, ContractTransaction, constants } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { bigNumEquals, evmRevert } from "../test-helpers/matchers"; +import { publicAbi, toWei, increaseTimeBy, mineBlock, evmWordToAddress } from "../test-helpers/helpers"; +import { randomBytes } from "@ethersproject/random"; + +let personas: Personas; +let linkTokenFactory: ContractFactory; +let fluxAggregatorFactory: ContractFactory; +let validatorMockFactory: ContractFactory; +let testHelperFactory: ContractFactory; +let validatorFactory: ContractFactory; +let flagsFactory: ContractFactory; +let acFactory: ContractFactory; +let gasGuzzlerFactory: ContractFactory; +let emptyAddress: string; + +before(async () => { + personas = (await getUsers()).personas; + linkTokenFactory = await ethers.getContractFactory("LinkToken"); + fluxAggregatorFactory = await ethers.getContractFactory("FluxAggregator"); + validatorMockFactory = await ethers.getContractFactory("AggregatorValidatorMock"); + testHelperFactory = await ethers.getContractFactory("FluxAggregatorTestHelper"); + validatorFactory = await ethers.getContractFactory("DeviationFlaggingValidator"); + flagsFactory = await ethers.getContractFactory("Flags"); + acFactory = await ethers.getContractFactory("SimpleWriteAccessController"); + gasGuzzlerFactory = await ethers.getContractFactory("GasGuzzler"); + emptyAddress = constants.AddressZero; +}); + +describe("FluxAggregator", () => { + const paymentAmount = toWei("3"); + const deposit = toWei("100"); + const answer = 100; + const minAns = 1; + const maxAns = 1; + const rrDelay = 0; + const timeout = 1800; + const decimals = 18; + const description = "LINK/USD"; + const reserveRounds = 2; + const minSubmissionValue = BigNumber.from("1"); + const maxSubmissionValue = BigNumber.from("100000000000000000000"); + + let aggregator: Contract; + let link: Contract; + let testHelper: Contract; + let validator: Contract; + let gasGuzzler: Contract; + let nextRound: number; + let oracles: Signer[]; + + async function updateFutureRounds( + aggregator: Contract, + overrides: { + minAnswers?: BigNumberish; + maxAnswers?: BigNumberish; + payment?: BigNumberish; + restartDelay?: BigNumberish; + timeout?: BigNumberish; + } = {}, + ) { + overrides = overrides || {}; + const round = { + payment: overrides.payment || paymentAmount, + minAnswers: overrides.minAnswers || minAns, + maxAnswers: overrides.maxAnswers || maxAns, + restartDelay: overrides.restartDelay || rrDelay, + timeout: overrides.timeout || timeout, + }; + + return aggregator.updateFutureRounds( + round.payment, + round.minAnswers, + round.maxAnswers, + round.restartDelay, + round.timeout, + ); + } + + async function addOracles( + aggregator: Contract, + oraclesAndAdmin: Signer[], + minAnswers: number, + maxAnswers: number, + restartDelay: number, + ): Promise { + return aggregator.connect(personas.Carol).changeOracles( + [], + oraclesAndAdmin.map(async oracle => await oracle.getAddress()), + oraclesAndAdmin.map(async admin => await admin.getAddress()), + minAnswers, + maxAnswers, + restartDelay, + ); + } + + async function advanceRound( + aggregator: Contract, + submitters: Signer[], + currentSubmission: number = answer, + ): Promise { + for (const submitter of submitters) { + await aggregator.connect(submitter).submit(nextRound, currentSubmission); + } + nextRound++; + return nextRound; + } + + const ShouldBeSet = "expects it to be different"; + const ShouldNotBeSet = "expects it to equal"; + let startingState: any; + + async function checkOracleRoundState( + state: any, + want: { + eligibleToSubmit: boolean; + roundId: BigNumberish; + latestSubmission: BigNumberish; + startedAt: string; + timeout: BigNumberish; + availableFunds: BigNumberish; + oracleCount: BigNumberish; + paymentAmount: BigNumberish; + }, + ) { + assert.equal(want.eligibleToSubmit, state._eligibleToSubmit, "round state: unexecpted eligibility"); + bigNumEquals(want.roundId, state._roundId, "round state: unexpected Round ID"); + bigNumEquals(want.latestSubmission, state._latestSubmission, "round state: unexpected latest submission"); + if (want.startedAt === ShouldBeSet) { + assert.isAbove( + state._startedAt.toNumber(), + startingState._startedAt.toNumber(), + "round state: expected the started at to be the same as previous", + ); + } else { + bigNumEquals(0, state._startedAt, "round state: expected the started at not to be updated"); + } + bigNumEquals(want.timeout, state._timeout.toNumber(), "round state: unexepcted timeout"); + bigNumEquals(want.availableFunds, state._availableFunds, "round state: unexepected funds"); + bigNumEquals(want.oracleCount, state._oracleCount, "round state: unexpected oracle count"); + bigNumEquals(want.paymentAmount, state._paymentAmount, "round state: unexpected paymentamount"); + } + + beforeEach(async () => { + link = await linkTokenFactory.connect(personas.Default).deploy(); + aggregator = await fluxAggregatorFactory + .connect(personas.Carol) + .deploy( + link.address, + paymentAmount, + timeout, + emptyAddress, + minSubmissionValue, + maxSubmissionValue, + decimals, + ethers.utils.formatBytes32String(description), + ); + await link.transfer(aggregator.address, deposit); + await aggregator.updateAvailableFunds(); + bigNumEquals(deposit, await link.balanceOf(aggregator.address)); + nextRound = 1; + }); + + it("has a limited public interface", () => { + publicAbi(aggregator, [ + "acceptAdmin", + "allocatedFunds", + "availableFunds", + "changeOracles", + "decimals", + "description", + "getAdmin", + "getAnswer", + "getOracles", + "getRoundData", + "getTimestamp", + "latestAnswer", + "latestRound", + "latestRoundData", + "latestTimestamp", + "linkToken", + "maxSubmissionCount", + "maxSubmissionValue", + "minSubmissionCount", + "minSubmissionValue", + "onTokenTransfer", + "oracleCount", + "oracleRoundState", + "paymentAmount", + "requestNewRound", + "restartDelay", + "setRequesterPermissions", + "setValidator", + "submit", + "timeout", + "transferAdmin", + "updateAvailableFunds", + "updateFutureRounds", + "withdrawFunds", + "withdrawPayment", + "withdrawablePayment", + "validator", + "version", + // Owned methods: + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#constructor", () => { + it("sets the paymentAmount", async () => { + bigNumEquals(BigNumber.from(paymentAmount), await aggregator.paymentAmount()); + }); + + it("sets the timeout", async () => { + bigNumEquals(BigNumber.from(timeout), await aggregator.timeout()); + }); + + it("sets the decimals", async () => { + bigNumEquals(BigNumber.from(decimals), await aggregator.decimals()); + }); + + it("sets the description", async () => { + assert.equal(ethers.utils.formatBytes32String(description), await aggregator.description()); + }); + + it("sets the version to 3", async () => { + bigNumEquals(3, await aggregator.version()); + }); + + it("sets the validator", async () => { + assert.equal(emptyAddress, await aggregator.validator()); + }); + }); + + describe("#submit", () => { + let minMax; + + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned, personas.Nelly]; + minMax = oracles.length; + await addOracles(aggregator, oracles, minMax, minMax, rrDelay); + }); + + it("updates the allocated and available funds counters", async () => { + bigNumEquals(0, await aggregator.allocatedFunds()); + + const tx = await aggregator.connect(personas.Neil).submit(nextRound, answer); + const receipt = await tx.wait(); + + bigNumEquals(paymentAmount, await aggregator.allocatedFunds()); + const expectedAvailable = deposit.sub(paymentAmount); + bigNumEquals(expectedAvailable, await aggregator.availableFunds()); + const logged = BigNumber.from(receipt.logs?.[2].topics[1] ?? BigNumber.from(-1)); + bigNumEquals(expectedAvailable, logged); + }); + + it("emits a log event announcing submission details", async () => { + await expect(aggregator.connect(personas.Nelly).submit(nextRound, answer)) + .to.emit(aggregator, "SubmissionReceived") + .withArgs(answer, nextRound, await personas.Nelly.getAddress()); + }); + + describe("when the minimum oracles have not reported", () => { + it("pays the oracles that have reported", async () => { + bigNumEquals(0, await aggregator.connect(personas.Neil).withdrawablePayment(await personas.Neil.getAddress())); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + bigNumEquals( + paymentAmount, + await aggregator.connect(personas.Neil).withdrawablePayment(await personas.Neil.getAddress()), + ); + bigNumEquals(0, await aggregator.connect(personas.Ned).withdrawablePayment(await personas.Ned.getAddress())); + bigNumEquals( + 0, + await aggregator.connect(personas.Nelly).withdrawablePayment(await personas.Nelly.getAddress()), + ); + }); + + it("does not update the answer", async () => { + bigNumEquals(ethers.constants.Zero, await aggregator.latestAnswer()); + + // Not updated because of changes by the owner setting minSubmissionCount to 3 + await aggregator.connect(personas.Ned).submit(nextRound, answer); + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + bigNumEquals(ethers.constants.Zero, await aggregator.latestAnswer()); + }); + }); + + describe("when an oracle prematurely bumps the round", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { minAnswers: 2, maxAnswers: 3 }); + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + + it("reverts", async () => { + await evmRevert( + aggregator.connect(personas.Neil).submit(nextRound + 1, answer), + "previous round not supersedable", + ); + }); + }); + + describe("when the minimum number of oracles have reported", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { minAnswers: 2, maxAnswers: 3 }); + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + + it("updates the answer with the median", async () => { + bigNumEquals(0, await aggregator.latestAnswer()); + + await aggregator.connect(personas.Ned).submit(nextRound, 99); + bigNumEquals(99, await aggregator.latestAnswer()); // ((100+99) / 2).to_i + + await aggregator.connect(personas.Nelly).submit(nextRound, 101); + + bigNumEquals(100, await aggregator.latestAnswer()); + }); + + it("updates the updated timestamp", async () => { + const originalTimestamp = await aggregator.latestTimestamp(); + assert.isAbove(originalTimestamp.toNumber(), 0); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + const currentTimestamp = await aggregator.latestTimestamp(); + assert.isAbove(currentTimestamp.toNumber(), originalTimestamp.toNumber()); + }); + + it("announces the new answer with a log event", async () => { + const tx = await aggregator.connect(personas.Nelly).submit(nextRound, answer); + const receipt = await tx.wait(); + + const newAnswer = BigNumber.from(receipt.logs?.[0].topics[1] ?? ethers.constants.Zero); + + assert.equal(answer, newAnswer.toNumber()); + }); + + it("does not set the timedout flag", async () => { + evmRevert(aggregator.getRoundData(nextRound), "No data present"); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + const round = await aggregator.getRoundData(nextRound); + assert.equal(nextRound, round.answeredInRound.toNumber()); + }); + + it("updates the round details", async () => { + evmRevert(aggregator.latestRoundData(), "No data present"); + + increaseTimeBy(15, ethers.provider); + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + const roundAfter = await aggregator.getRoundData(nextRound); + bigNumEquals(nextRound, roundAfter.roundId); + bigNumEquals(answer, roundAfter.answer); + assert.isFalse(roundAfter.startedAt.isZero()); + bigNumEquals(await aggregator.getTimestamp(nextRound), roundAfter.updatedAt); + bigNumEquals(nextRound, roundAfter.answeredInRound); + + assert.isBelow(roundAfter.startedAt.toNumber(), roundAfter.updatedAt.toNumber()); + + const roundAfterLatest = await aggregator.latestRoundData(); + bigNumEquals(roundAfter.roundId, roundAfterLatest.roundId); + bigNumEquals(roundAfter.answer, roundAfterLatest.answer); + bigNumEquals(roundAfter.startedAt, roundAfterLatest.startedAt); + bigNumEquals(roundAfter.updatedAt, roundAfterLatest.updatedAt); + bigNumEquals(roundAfter.answeredInRound, roundAfterLatest.answeredInRound); + }); + }); + + describe("when an oracle submits for a round twice", () => { + it("reverts", async () => { + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + await evmRevert( + aggregator.connect(personas.Neil).submit(nextRound, answer), + "cannot report on previous rounds", + ); + }); + }); + + describe("when updated after the max answers submitted", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator); + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + + it("reverts", async () => { + await evmRevert(aggregator.connect(personas.Ned).submit(nextRound, answer), "round not accepting submissions"); + }); + }); + + describe("when a new highest round number is passed in", () => { + it("increments the answer round", async () => { + const startingState = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + bigNumEquals(1, startingState._roundId); + + await advanceRound(aggregator, oracles); + + const updatedState = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + bigNumEquals(2, updatedState._roundId); + }); + + it("sets the startedAt time for the reporting round", async () => { + evmRevert(aggregator.getRoundData(nextRound), "No data present"); + + const tx = await aggregator.connect(oracles[0]).submit(nextRound, answer); + await aggregator.connect(oracles[1]).submit(nextRound, answer); + await aggregator.connect(oracles[2]).submit(nextRound, answer); + const receipt = await tx.wait(); + const block = await ethers.provider.getBlock(receipt.blockHash ?? ""); + + const round = await aggregator.getRoundData(nextRound); + bigNumEquals(BigNumber.from(block.timestamp), round.startedAt); + }); + + it("announces a new round by emitting a log", async () => { + const tx = await aggregator.connect(personas.Neil).submit(nextRound, answer); + const receipt = await tx.wait(); + + const topics = receipt.logs?.[0].topics ?? []; + const roundNumber = BigNumber.from(topics[1]); + const startedBy = evmWordToAddress(topics[2]); + + bigNumEquals(nextRound, roundNumber.toNumber()); + bigNumEquals(startedBy, await personas.Neil.getAddress()); + }); + }); + + describe("when a round is passed in higher than expected", () => { + it("reverts", async () => { + await evmRevert(aggregator.connect(personas.Neil).submit(nextRound + 1, answer), "invalid round to report"); + }); + }); + + describe("when called by a non-oracle", () => { + it("reverts", async () => { + await evmRevert(aggregator.connect(personas.Carol).submit(nextRound, answer), "not enabled oracle"); + }); + }); + + describe("when there are not sufficient available funds", () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .withdrawFunds( + await personas.Carol.getAddress(), + deposit.sub(paymentAmount.mul(oracles.length).mul(reserveRounds)), + ); + + // drain remaining funds + await advanceRound(aggregator, oracles); + await advanceRound(aggregator, oracles); + }); + + it("reverts", async () => { + await evmRevert(aggregator.connect(personas.Neil).submit(nextRound, answer), "SafeMath: subtraction overflow"); + }); + }); + + describe("when a new round opens before the previous rounds closes", () => { + beforeEach(async () => { + oracles = [personas.Nancy, personas.Norbert]; + await addOracles(aggregator, oracles, 3, 4, rrDelay); + await advanceRound(aggregator, [personas.Nelly, personas.Neil, personas.Nancy]); + + // start the next round + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + }); + + it("still allows the previous round to be answered", async () => { + await aggregator.connect(personas.Ned).submit(nextRound - 1, answer); + }); + + describe("once the current round is answered", () => { + beforeEach(async () => { + oracles = [personas.Neil, personas.Nancy]; + for (let i = 0; i < oracles.length; i++) { + await aggregator.connect(oracles[i]).submit(nextRound, answer); + } + }); + + it("does not allow reports for the previous round", async () => { + await evmRevert(aggregator.connect(personas.Ned).submit(nextRound - 1, answer), "invalid round to report"); + }); + }); + + describe("when the previous round has finished", () => { + beforeEach(async () => { + await aggregator.connect(personas.Norbert).submit(nextRound - 1, answer); + }); + + it("does not allow reports for the previous round", async () => { + await evmRevert( + aggregator.connect(personas.Ned).submit(nextRound - 1, answer), + "round not accepting submissions", + ); + }); + }); + }); + + describe("when price is updated mid-round", () => { + const newAmount = toWei("50"); + + it("pays the same amount to all oracles per round", async () => { + await link.transfer(aggregator.address, newAmount.mul(oracles.length).mul(reserveRounds)); + await aggregator.updateAvailableFunds(); + + bigNumEquals(0, await aggregator.connect(personas.Neil).withdrawablePayment(await personas.Neil.getAddress())); + bigNumEquals( + 0, + await aggregator.connect(personas.Nelly).withdrawablePayment(await personas.Nelly.getAddress()), + ); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + await updateFutureRounds(aggregator, { payment: newAmount }); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + bigNumEquals( + paymentAmount, + await aggregator.connect(personas.Neil).withdrawablePayment(await personas.Neil.getAddress()), + ); + bigNumEquals( + paymentAmount, + await aggregator.connect(personas.Nelly).withdrawablePayment(await personas.Nelly.getAddress()), + ); + }); + }); + + describe("when delay is on", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { + minAnswers: oracles.length, + maxAnswers: oracles.length, + restartDelay: 1, + }); + }); + + it("does not revert on the oracle's first round", async () => { + // Since lastUpdatedRound defaults to zero and that's the only + // indication that an oracle hasn't responded, this test guards against + // the situation where we don't check that and no one can start a round. + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + + it("does revert before the delay", async () => { + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + nextRound++; + + await evmRevert(aggregator.connect(personas.Neil).submit(nextRound, answer), "previous round not supersedable"); + }); + }); + + describe("when an oracle starts a round before the restart delay is over", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator.connect(personas.Carol), { + minAnswers: 1, + maxAnswers: 1, + }); + + oracles = [personas.Neil, personas.Ned, personas.Nelly]; + for (let i = 0; i < oracles.length; i++) { + await aggregator.connect(oracles[i]).submit(nextRound, answer); + nextRound++; + } + + const newDelay = 2; + // Since Ned and Nelly have answered recently, and we set the delay + // to 2, only Nelly can answer as she is the only oracle that hasn't + // started the last two rounds. + await updateFutureRounds(aggregator, { + maxAnswers: oracles.length, + restartDelay: newDelay, + }); + }); + + describe("when called by an oracle who has not answered recently", () => { + it("does not revert", async () => { + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + }); + + describe("when called by an oracle who answered recently", () => { + it("reverts", async () => { + await evmRevert( + aggregator.connect(personas.Ned).submit(nextRound, answer), + "round not accepting submissions", + ); + + await evmRevert( + aggregator.connect(personas.Nelly).submit(nextRound, answer), + "round not accepting submissions", + ); + }); + }); + }); + + describe("when the price is not updated for a round", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { + minAnswers: oracles.length, + maxAnswers: oracles.length, + restartDelay: 1, + }); + + for (const oracle of oracles) { + await aggregator.connect(oracle).submit(nextRound, answer); + } + nextRound++; + + await aggregator.connect(personas.Ned).submit(nextRound, answer); + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + await increaseTimeBy(timeout + 1, ethers.provider); + nextRound++; + }); + + it("allows a new round to be started", async () => { + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + }); + + it("sets the info for the previous round", async () => { + const previousRound = nextRound - 1; + let updated = await aggregator.getTimestamp(previousRound); + let ans = await aggregator.getAnswer(previousRound); + assert.equal(0, updated.toNumber()); + assert.equal(0, ans.toNumber()); + + const tx = await aggregator.connect(personas.Nelly).submit(nextRound, answer); + const receipt = await tx.wait(); + + const block = await ethers.provider.getBlock(receipt.blockHash ?? ""); + + updated = await aggregator.getTimestamp(previousRound); + ans = await aggregator.getAnswer(previousRound); + bigNumEquals(BigNumber.from(block.timestamp), updated); + assert.equal(answer, ans.toNumber()); + + const round = await aggregator.getRoundData(previousRound); + bigNumEquals(previousRound, round.roundId); + bigNumEquals(ans, round.answer); + bigNumEquals(updated, round.updatedAt); + bigNumEquals(previousRound - 1, round.answeredInRound); + }); + + it("sets the previous round as timed out", async () => { + const previousRound = nextRound - 1; + evmRevert(aggregator.getRoundData(previousRound), "No data present"); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + + const round = await aggregator.getRoundData(previousRound); + assert.notEqual(round.roundId, round.answeredInRound); + bigNumEquals(previousRound - 1, round.answeredInRound); + }); + + it("still respects the delay restriction", async () => { + // expected to revert because the sender started the last round + await evmRevert(aggregator.connect(personas.Ned).submit(nextRound, answer)); + }); + + it("uses the timeout set at the beginning of the round", async () => { + await updateFutureRounds(aggregator, { + timeout: timeout + 100000, + }); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + }); + }); + + describe("submitting values near the edges of allowed values", () => { + it("rejects values below the submission value range", async () => { + await evmRevert( + aggregator.connect(personas.Neil).submit(nextRound, minSubmissionValue.sub(1)), + "value below minSubmissionValue", + ); + }); + + it("accepts submissions equal to the min submission value", async () => { + await aggregator.connect(personas.Neil).submit(nextRound, minSubmissionValue); + }); + + it("accepts submissions equal to the max submission value", async () => { + await aggregator.connect(personas.Neil).submit(nextRound, maxSubmissionValue); + }); + + it("rejects submissions equal to the max submission value", async () => { + await evmRevert( + aggregator.connect(personas.Neil).submit(nextRound, maxSubmissionValue.add(1)), + "value above maxSubmissionValue", + ); + }); + }); + + describe("when a validator is set", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { minAnswers: 1, maxAnswers: 1 }); + oracles = [personas.Nelly]; + + validator = await validatorMockFactory.connect(personas.Carol).deploy(); + await aggregator.connect(personas.Carol).setValidator(validator.address); + }); + + it("calls out to the validator", async () => { + await expect(aggregator.connect(personas.Nelly).submit(nextRound, answer)) + .to.emit(validator, "Validated") + .withArgs(0, 0, nextRound, answer); + }); + }); + + describe("when the answer validator eats all gas", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { minAnswers: 1, maxAnswers: 1 }); + oracles = [personas.Nelly]; + + gasGuzzler = await gasGuzzlerFactory.connect(personas.Carol).deploy(); + await aggregator.connect(personas.Carol).setValidator(gasGuzzler.address); + assert.equal(gasGuzzler.address, await aggregator.validator()); + }); + + it("still updates", async () => { + bigNumEquals(0, await aggregator.latestAnswer()); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer, { gasLimit: 500000 }); + + bigNumEquals(answer, await aggregator.latestAnswer()); + }); + }); + }); + + describe("#getAnswer", () => { + const answers = [1, 10, 101, 1010, 10101, 101010, 1010101]; + + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + + for (const answer of answers) { + await aggregator.connect(personas.Neil).submit(nextRound++, answer); + } + }); + + it("retrieves the answer recorded for past rounds", async () => { + for (let i = nextRound; i < nextRound; i++) { + const answer = await aggregator.getAnswer(i); + bigNumEquals(BigNumber.from(answers[i - 1]), answer); + } + }); + + it("returns 0 for answers greater than uint32's max", async () => { + const overflowedId = BigNumber.from(2).pow(32).add(1); + const answer = await aggregator.getAnswer(overflowedId); + bigNumEquals(0, answer); + }); + }); + + describe("#getTimestamp", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + + for (let i = 0; i < 10; i++) { + await aggregator.connect(personas.Neil).submit(nextRound++, i + 1); + } + }); + + it("retrieves the answer recorded for past rounds", async () => { + let lastTimestamp = ethers.constants.Zero; + + for (let i = 1; i < nextRound; i++) { + const currentTimestamp = await aggregator.getTimestamp(i); + assert.isAtLeast(currentTimestamp.toNumber(), lastTimestamp.toNumber()); + lastTimestamp = currentTimestamp; + } + }); + + it("returns 0 for answers greater than uint32's max", async () => { + const overflowedId = BigNumber.from(2).pow(32).add(1); + const answer = await aggregator.getTimestamp(overflowedId); + bigNumEquals(0, answer); + }); + }); + + describe("#changeOracles", () => { + describe("adding oracles", () => { + it("increases the oracle count", async () => { + const pastCount = await aggregator.oracleCount(); + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + const currentCount = await aggregator.oracleCount(); + + bigNumEquals(currentCount, pastCount + 1); + }); + + it("adds the address in getOracles", async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + assert.deepEqual([await personas.Neil.getAddress()], await aggregator.getOracles()); + }); + + it("updates the round details", async () => { + await addOracles(aggregator, [personas.Neil, personas.Ned, personas.Nelly], 1, 3, 2); + bigNumEquals(1, await aggregator.minSubmissionCount()); + bigNumEquals(3, await aggregator.maxSubmissionCount()); + bigNumEquals(2, await aggregator.restartDelay()); + }); + + it("emits a log", async () => { + const tx = await aggregator + .connect(personas.Carol) + .changeOracles([], [await personas.Ned.getAddress()], [await personas.Neil.getAddress()], 1, 1, 0); + expect(tx) + .to.emit(aggregator, "OraclePermissionsUpdated") + .withArgs(await personas.Ned.getAddress(), true); + + expect(tx) + .to.emit(aggregator, "OracleAdminUpdated") + .withArgs(await personas.Ned.getAddress(), await personas.Neil.getAddress()); + }); + + describe("when the oracle has already been added", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + }); + + it("reverts", async () => { + await evmRevert(addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay), "oracle already enabled"); + }); + }); + + describe("when called by anyone but the owner", () => { + it("reverts", async () => { + await evmRevert( + aggregator + .connect(personas.Neil) + .changeOracles( + [], + [await personas.Neil.getAddress()], + [await personas.Neil.getAddress()], + minAns, + maxAns, + rrDelay, + ), + "Only callable by owner", + ); + }); + }); + + describe("when an oracle gets added mid-round", () => { + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned]; + await addOracles(aggregator, oracles, oracles.length, oracles.length, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + await addOracles(aggregator, [personas.Nelly], oracles.length + 1, oracles.length + 1, rrDelay); + }); + + it("does not allow the oracle to update the round", async () => { + await evmRevert(aggregator.connect(personas.Nelly).submit(nextRound, answer), "not yet enabled oracle"); + }); + + it("does allow the oracle to update future rounds", async () => { + // complete round + await aggregator.connect(personas.Ned).submit(nextRound, answer); + + // now can participate in new rounds + await aggregator.connect(personas.Nelly).submit(nextRound + 1, answer); + }); + }); + + describe("when an oracle is added after removed for a round", () => { + it("allows the oracle to update", async () => { + oracles = [personas.Neil, personas.Nelly]; + await addOracles(aggregator, oracles, oracles.length, oracles.length, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + nextRound++; + + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Nelly.getAddress()], [], [], 1, 1, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + nextRound++; + + await addOracles(aggregator, [personas.Nelly], 1, 1, rrDelay); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + }); + }); + + describe("when an oracle is added and immediately removed mid-round", () => { + it("allows the oracle to update", async () => { + await addOracles(aggregator, oracles, oracles.length, oracles.length, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + nextRound++; + + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Nelly.getAddress()], [], [], 1, 1, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + nextRound++; + + await addOracles(aggregator, [personas.Nelly], 1, 1, rrDelay); + + await aggregator.connect(personas.Nelly).submit(nextRound, answer); + }); + }); + + describe("when an oracle is re-added with a different admin address", () => { + it("reverts", async () => { + await addOracles(aggregator, oracles, oracles.length, oracles.length, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Nelly.getAddress()], [], [], 1, 1, rrDelay); + + await evmRevert( + aggregator + .connect(personas.Carol) + .changeOracles( + [], + [await personas.Nelly.getAddress()], + [await personas.Carol.getAddress()], + 1, + 1, + rrDelay, + ), + "owner cannot overwrite admin", + ); + }); + }); + + const limit = 77; + describe(`when adding more than ${limit} oracles`, () => { + let oracles: Signer[]; + + beforeEach(async () => { + oracles = []; + for (let i = 0; i < limit; i++) { + const account = await new ethers.Wallet(randomBytes(32), ethers.provider); + await personas.Default.sendTransaction({ + to: account.address, + value: toWei("0.1"), + }); + + oracles.push(account); + } + + await link.transfer(aggregator.address, paymentAmount.mul(limit).mul(reserveRounds)); + await aggregator.updateAvailableFunds(); + + let addresses = oracles.slice(0, 50).map(async o => o.getAddress()); + await aggregator.connect(personas.Carol).changeOracles([], addresses, addresses, 1, 50, rrDelay); + // add in two transactions to avoid gas limit issues + addresses = oracles.slice(50, 100).map(async o => o.getAddress()); + await aggregator.connect(personas.Carol).changeOracles([], addresses, addresses, 1, oracles.length, rrDelay); + }); + + it("not use too much gas", async () => { + let tx: any; + assert.deepEqual( + // test adveserial quickselect algo + [2, 4, 6, 8, 10, 12, 14, 16, 1, 9, 5, 11, 3, 13, 7, 15], + adverserialQuickselectList(16), + ); + const inputs = adverserialQuickselectList(limit); + for (let i = 0; i < limit; i++) { + tx = await aggregator.connect(oracles[i]).submit(nextRound, inputs[i]); + } + assert.isTrue(!!tx); + if (tx) { + const receipt = await tx.wait(); + assert.isAbove(400_000, receipt.gasUsed.toNumber()); + } + }); + + function adverserialQuickselectList(len: number): number[] { + const xs: number[] = []; + const pi: number[] = []; + for (let i = 0; i < len; i++) { + pi[i] = i; + xs[i] = 0; + } + + for (let l = len; l > 0; l--) { + const pivot = Math.floor((l - 1) / 2); + xs[pi[pivot]] = l; + const temp = pi[l - 1]; + pi[l - 1] = pi[pivot]; + pi[pivot] = temp; + } + return xs; + } + + it("reverts when another oracle is added", async () => { + await evmRevert( + aggregator + .connect(personas.Carol) + .changeOracles( + [], + [await personas.Neil.getAddress()], + [await personas.Neil.getAddress()], + limit + 1, + limit + 1, + rrDelay, + ), + "max oracles allowed", + ); + }); + }); + + it("reverts when minSubmissions is set to 0", async () => { + await evmRevert(addOracles(aggregator, [personas.Neil], 0, 0, 0), "min must be greater than 0"); + }); + }); + + describe("removing oracles", () => { + beforeEach(async () => { + oracles = [personas.Neil, personas.Nelly]; + await addOracles(aggregator, oracles, oracles.length, oracles.length, rrDelay); + }); + + it("decreases the oracle count", async () => { + const pastCount = await aggregator.oracleCount(); + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay); + const currentCount = await aggregator.oracleCount(); + + expect(currentCount).to.equal(pastCount - 1); + }); + + it("updates the round details", async () => { + await aggregator.connect(personas.Carol).changeOracles([await personas.Neil.getAddress()], [], [], 1, 1, 0); + + bigNumEquals(1, await aggregator.minSubmissionCount()); + bigNumEquals(1, await aggregator.maxSubmissionCount()); + bigNumEquals(ethers.constants.Zero, await aggregator.restartDelay()); + }); + + it("emits a log", async () => { + await expect( + aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay), + ) + .to.emit(aggregator, "OraclePermissionsUpdated") + .withArgs(await personas.Neil.getAddress(), false); + }); + + it("removes the address in getOracles", async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay); + assert.deepEqual([await personas.Nelly.getAddress()], await aggregator.getOracles()); + }); + + describe("when the oracle is not currently added", () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay); + }); + + it("reverts", async () => { + await evmRevert( + aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay), + "oracle not enabled", + ); + }); + }); + + describe("when removing the last oracle", () => { + it("does not revert", async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay); + + await aggregator.connect(personas.Carol).changeOracles([await personas.Nelly.getAddress()], [], [], 0, 0, 0); + }); + }); + + describe("when called by anyone but the owner", () => { + it("reverts", async () => { + await evmRevert( + aggregator.connect(personas.Ned).changeOracles([await personas.Neil.getAddress()], [], [], 0, 0, rrDelay), + "Only callable by owner", + ); + }); + }); + + describe("when an oracle gets removed", () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Nelly.getAddress()], [], [], 1, 1, rrDelay); + }); + + it("is allowed to report on one more round", async () => { + // next round + await advanceRound(aggregator, [personas.Nelly]); + // finish round + await advanceRound(aggregator, [personas.Neil]); + + // cannot participate in future rounds + await evmRevert(aggregator.connect(personas.Nelly).submit(nextRound, answer), "no longer allowed oracle"); + }); + }); + + describe("when an oracle gets removed mid-round", () => { + beforeEach(async () => { + await aggregator.connect(personas.Neil).submit(nextRound, answer); + + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Nelly.getAddress()], [], [], 1, 1, rrDelay); + }); + + it("is allowed to finish that round and one more round", async () => { + await advanceRound(aggregator, [personas.Nelly]); // finish round + + await advanceRound(aggregator, [personas.Nelly]); // next round + + // cannot participate in future rounds + await evmRevert(aggregator.connect(personas.Nelly).submit(nextRound, answer), "no longer allowed oracle"); + }); + }); + + it("reverts when minSubmissions is set to 0", async () => { + await evmRevert( + aggregator.connect(personas.Carol).changeOracles([await personas.Nelly.getAddress()], [], [], 0, 0, 0), + "min must be greater than 0", + ); + }); + }); + + describe("adding and removing oracles at once", () => { + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned]; + await addOracles(aggregator, oracles, 1, 1, rrDelay); + }); + + it("can swap out oracles", async () => { + assert.include(await aggregator.getOracles(), await personas.Ned.getAddress()); + assert.notInclude(await aggregator.getOracles(), await personas.Nelly.getAddress()); + + await aggregator + .connect(personas.Carol) + .changeOracles( + [await personas.Ned.getAddress()], + [await personas.Nelly.getAddress()], + [await personas.Nelly.getAddress()], + 1, + 1, + rrDelay, + ); + + assert.notInclude(await aggregator.getOracles(), await personas.Ned.getAddress()); + assert.include(await aggregator.getOracles(), await personas.Nelly.getAddress()); + }); + + it("is possible to remove and add the same address", async () => { + assert.include(await aggregator.getOracles(), await personas.Ned.getAddress()); + + await aggregator + .connect(personas.Carol) + .changeOracles( + [await personas.Ned.getAddress()], + [await personas.Ned.getAddress()], + [await personas.Ned.getAddress()], + 1, + 1, + rrDelay, + ); + + assert.include(await aggregator.getOracles(), await personas.Ned.getAddress()); + }); + }); + }); + + describe("#getOracles", () => { + describe("after adding oracles", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + + assert.deepEqual([await personas.Neil.getAddress()], await aggregator.getOracles()); + }); + + it("returns the addresses of added oracles", async () => { + await addOracles(aggregator, [personas.Ned], minAns, maxAns, rrDelay); + + assert.deepEqual( + [await personas.Neil.getAddress(), await personas.Ned.getAddress()], + await aggregator.getOracles(), + ); + + await addOracles(aggregator, [personas.Nelly], minAns, maxAns, rrDelay); + assert.deepEqual( + [await personas.Neil.getAddress(), await personas.Ned.getAddress(), await personas.Nelly.getAddress()], + await aggregator.getOracles(), + ); + }); + }); + + describe("after removing oracles", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil, personas.Ned, personas.Nelly], minAns, maxAns, rrDelay); + + assert.deepEqual( + [await personas.Neil.getAddress(), await personas.Ned.getAddress(), await personas.Nelly.getAddress()], + await aggregator.getOracles(), + ); + }); + + it("reorders when removing from the beginning", async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Neil.getAddress()], [], [], minAns, maxAns, rrDelay); + assert.deepEqual( + [await personas.Nelly.getAddress(), await personas.Ned.getAddress()], + await aggregator.getOracles(), + ); + }); + + it("reorders when removing from the middle", async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Ned.getAddress()], [], [], minAns, maxAns, rrDelay); + assert.deepEqual( + [await personas.Neil.getAddress(), await personas.Nelly.getAddress()], + await aggregator.getOracles(), + ); + }); + + it("pops the last node off at the end", async () => { + await aggregator + .connect(personas.Carol) + .changeOracles([await personas.Nelly.getAddress()], [], [], minAns, maxAns, rrDelay); + assert.deepEqual( + [await personas.Neil.getAddress(), await personas.Ned.getAddress()], + await aggregator.getOracles(), + ); + }); + }); + }); + + describe("#withdrawFunds", () => { + it("succeeds", async () => { + await aggregator.connect(personas.Carol).withdrawFunds(await personas.Carol.getAddress(), deposit); + + bigNumEquals(0, await aggregator.availableFunds()); + bigNumEquals(deposit, await link.balanceOf(await personas.Carol.getAddress())); + }); + + it("does not let withdrawals happen multiple times", async () => { + await aggregator.connect(personas.Carol).withdrawFunds(await personas.Carol.getAddress(), deposit); + + await evmRevert( + aggregator.connect(personas.Carol).withdrawFunds(await personas.Carol.getAddress(), deposit), + "insufficient reserve funds", + ); + }); + + describe("with a number higher than the available LINK balance", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + + it("fails", async () => { + await evmRevert( + aggregator.connect(personas.Carol).withdrawFunds(await personas.Carol.getAddress(), deposit), + "insufficient reserve funds", + ); + + bigNumEquals(deposit.sub(paymentAmount), await aggregator.availableFunds()); + }); + }); + + describe("with oracles still present", () => { + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned, personas.Nelly]; + await addOracles(aggregator, oracles, 1, 1, rrDelay); + + bigNumEquals(deposit, await aggregator.availableFunds()); + }); + + it("does not allow withdrawal with less than 2x rounds of payments", async () => { + const oracleReserve = paymentAmount.mul(oracles.length).mul(reserveRounds); + const allowed = deposit.sub(oracleReserve); + + //one more than the allowed amount cannot be withdrawn + await evmRevert( + aggregator.connect(personas.Carol).withdrawFunds(await personas.Carol.getAddress(), allowed.add(1)), + "insufficient reserve funds", + ); + + // the allowed amount can be withdrawn + await aggregator.connect(personas.Carol).withdrawFunds(await personas.Carol.getAddress(), allowed); + }); + }); + + describe("when called by a non-owner", () => { + it("fails", async () => { + await evmRevert( + aggregator.connect(personas.Eddy).withdrawFunds(await personas.Carol.getAddress(), deposit), + "Only callable by owner", + ); + + bigNumEquals(deposit, await aggregator.availableFunds()); + }); + }); + }); + + describe("#updateFutureRounds", () => { + let minSubmissionCount, maxSubmissionCount; + const newPaymentAmount = toWei("2"); + const newMin = 1; + const newMax = 3; + const newDelay = 2; + + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned, personas.Nelly]; + minSubmissionCount = oracles.length; + maxSubmissionCount = oracles.length; + await addOracles(aggregator, oracles, minSubmissionCount, maxSubmissionCount, rrDelay); + + bigNumEquals(paymentAmount, await aggregator.paymentAmount()); + assert.equal(minSubmissionCount, await aggregator.minSubmissionCount()); + assert.equal(maxSubmissionCount, await aggregator.maxSubmissionCount()); + }); + + it("updates the min and max answer counts", async () => { + await updateFutureRounds(aggregator, { + payment: newPaymentAmount, + minAnswers: newMin, + maxAnswers: newMax, + restartDelay: newDelay, + }); + + bigNumEquals(newPaymentAmount, await aggregator.paymentAmount()); + bigNumEquals(BigNumber.from(newMin), await aggregator.minSubmissionCount()); + bigNumEquals(BigNumber.from(newMax), await aggregator.maxSubmissionCount()); + bigNumEquals(BigNumber.from(newDelay), await aggregator.restartDelay()); + }); + + it("emits a log announcing the new round details", async () => { + await expect( + updateFutureRounds(aggregator, { + payment: newPaymentAmount, + minAnswers: newMin, + maxAnswers: newMax, + restartDelay: newDelay, + timeout: timeout + 1, + }), + ) + .to.emit(aggregator, "RoundDetailsUpdated") + .withArgs(newPaymentAmount, newMin, newMax, newDelay, timeout + 1); + }); + + describe("when it is set to higher than the number or oracles", () => { + it("reverts", async () => { + await evmRevert( + updateFutureRounds(aggregator, { + maxAnswers: 4, + }), + "max cannot exceed total", + ); + }); + }); + + describe("when it sets the min higher than the max", () => { + it("reverts", async () => { + await evmRevert( + updateFutureRounds(aggregator, { + minAnswers: 3, + maxAnswers: 2, + }), + "max must equal/exceed min", + ); + }); + }); + + describe("when delay equal or greater the oracle count", () => { + it("reverts", async () => { + await evmRevert( + updateFutureRounds(aggregator, { + restartDelay: 3, + }), + "revert delay cannot exceed total", + ); + }); + }); + + describe("when the payment amount does not cover reserve rounds", () => { + beforeEach(async () => {}); + + it("reverts", async () => { + const most = deposit.div(oracles.length * reserveRounds); + + await evmRevert( + updateFutureRounds(aggregator, { + payment: most.add(1), + }), + "insufficient funds for payment", + ); + + await updateFutureRounds(aggregator, { + payment: most, + }); + }); + }); + + describe("min oracles is set to 0", () => { + it("reverts", async () => { + await evmRevert( + aggregator.updateFutureRounds(paymentAmount, 0, 0, rrDelay, timeout), + "min must be greater than 0", + ); + }); + }); + + describe("when called by anyone but the owner", () => { + it("reverts", async () => { + await evmRevert(updateFutureRounds(aggregator.connect(personas.Ned)), "Only callable by owner"); + }); + }); + }); + + describe("#updateAvailableFunds", () => { + it("checks the LINK token to see if any additional funds are available", async () => { + const originalBalance = await aggregator.availableFunds(); + + await aggregator.updateAvailableFunds(); + + bigNumEquals(originalBalance, await aggregator.availableFunds()); + + await link.transfer(aggregator.address, deposit); + await aggregator.updateAvailableFunds(); + + const newBalance = await aggregator.availableFunds(); + bigNumEquals(originalBalance.add(deposit), newBalance); + }); + + it("removes allocated funds from the available balance", async () => { + const originalBalance = await aggregator.availableFunds(); + + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + await aggregator.connect(personas.Neil).submit(nextRound, answer); + await link.transfer(aggregator.address, deposit); + await aggregator.updateAvailableFunds(); + + const expected = originalBalance.add(deposit).sub(paymentAmount); + const newBalance = await aggregator.availableFunds(); + bigNumEquals(expected, newBalance); + }); + + it("emits a log", async () => { + await link.transfer(aggregator.address, deposit); + + const tx = await aggregator.updateAvailableFunds(); + const receipt = await tx.wait(); + + const reportedBalance = BigNumber.from(receipt.logs?.[0].topics[1] ?? -1); + bigNumEquals(await aggregator.availableFunds(), reportedBalance); + }); + + describe("when the available funds have not changed", () => { + it("does not emit a log", async () => { + const tx = await aggregator.updateAvailableFunds(); + const receipt = await tx.wait(); + + assert.equal(0, receipt.logs?.length); + }); + }); + }); + + describe("#withdrawPayment", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay); + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + + it("transfers LINK to the recipient", async () => { + const originalBalance = await link.balanceOf(aggregator.address); + bigNumEquals(0, await link.balanceOf(await personas.Neil.getAddress())); + + await aggregator + .connect(personas.Neil) + .withdrawPayment(await personas.Neil.getAddress(), await personas.Neil.getAddress(), paymentAmount); + + bigNumEquals(originalBalance.sub(paymentAmount), await link.balanceOf(aggregator.address)); + bigNumEquals(paymentAmount, await link.balanceOf(await personas.Neil.getAddress())); + }); + + it("decrements the allocated funds counter", async () => { + const originalAllocation = await aggregator.allocatedFunds(); + + await aggregator + .connect(personas.Neil) + .withdrawPayment(await personas.Neil.getAddress(), await personas.Neil.getAddress(), paymentAmount); + + bigNumEquals(originalAllocation.sub(paymentAmount), await aggregator.allocatedFunds()); + }); + + describe("when the caller withdraws more than they have", () => { + it("reverts", async () => { + await evmRevert( + aggregator + .connect(personas.Neil) + .withdrawPayment( + await personas.Neil.getAddress(), + await personas.Neil.getAddress(), + paymentAmount.add(BigNumber.from(1)), + ), + "revert insufficient withdrawable funds", + ); + }); + }); + + describe("when the caller is not the admin", () => { + it("reverts", async () => { + await evmRevert( + aggregator + .connect(personas.Nelly) + .withdrawPayment(await personas.Neil.getAddress(), await personas.Nelly.getAddress(), BigNumber.from(1)), + "only callable by admin", + ); + }); + }); + }); + + describe("#transferAdmin", () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [await personas.Ned.getAddress()], + [await personas.Neil.getAddress()], + minAns, + maxAns, + rrDelay, + ); + }); + + describe("when the admin tries to transfer the admin", () => { + it("works", async () => { + await expect( + aggregator + .connect(personas.Neil) + .transferAdmin(await personas.Ned.getAddress(), await personas.Nelly.getAddress()), + ) + .to.emit(aggregator, "OracleAdminUpdateRequested") + .withArgs( + await personas.Ned.getAddress(), + await personas.Neil.getAddress(), + await personas.Nelly.getAddress(), + ); + assert.equal(await personas.Neil.getAddress(), await aggregator.getAdmin(await personas.Ned.getAddress())); + }); + }); + + describe("when the non-admin owner tries to update the admin", () => { + it("reverts", async () => { + await evmRevert( + aggregator + .connect(personas.Carol) + .transferAdmin(await personas.Ned.getAddress(), await personas.Nelly.getAddress()), + "revert only callable by admin", + ); + }); + }); + + describe("when the non-admin oracle tries to update the admin", () => { + it("reverts", async () => { + await evmRevert( + aggregator + .connect(personas.Ned) + .transferAdmin(await personas.Ned.getAddress(), await personas.Nelly.getAddress()), + "revert only callable by admin", + ); + }); + }); + }); + + describe("#acceptAdmin", () => { + beforeEach(async () => { + await aggregator + .connect(personas.Carol) + .changeOracles( + [], + [await personas.Ned.getAddress()], + [await personas.Neil.getAddress()], + minAns, + maxAns, + rrDelay, + ); + const tx = await aggregator + .connect(personas.Neil) + .transferAdmin(await personas.Ned.getAddress(), await personas.Nelly.getAddress()); + await tx.wait(); + }); + + describe("when the new admin tries to accept", () => { + it("works", async () => { + await expect(aggregator.connect(personas.Nelly).acceptAdmin(await personas.Ned.getAddress())) + .to.emit(aggregator, "OracleAdminUpdated") + .withArgs(await personas.Ned.getAddress(), await personas.Nelly.getAddress()); + assert.equal(await personas.Nelly.getAddress(), await aggregator.getAdmin(await personas.Ned.getAddress())); + }); + }); + + describe("when someone other than the new admin tries to accept", () => { + it("reverts", async () => { + await evmRevert( + aggregator.connect(personas.Ned).acceptAdmin(await personas.Ned.getAddress()), + "only callable by pending admin", + ); + await evmRevert( + aggregator.connect(personas.Neil).acceptAdmin(await personas.Ned.getAddress()), + "only callable by pending admin", + ); + }); + }); + }); + + describe("#onTokenTransfer", () => { + it("updates the available balance", async () => { + const originalBalance = await aggregator.availableFunds(); + + await aggregator.updateAvailableFunds(); + + bigNumEquals(originalBalance, await aggregator.availableFunds()); + + await link.transferAndCall(aggregator.address, deposit, "0x"); + + const newBalance = await aggregator.availableFunds(); + bigNumEquals(originalBalance.add(deposit), newBalance); + }); + + it("reverts given calldata", async () => { + await evmRevert( + // error message is not bubbled up by link token + link.transferAndCall(aggregator.address, deposit, "0x12345678"), + ); + }); + }); + + describe("#requestNewRound", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], 1, 1, 0); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + nextRound = nextRound + 1; + + await aggregator.setRequesterPermissions(await personas.Carol.getAddress(), true, 0); + }); + + it("announces a new round via log event", async () => { + await expect(aggregator.requestNewRound()).to.emit(aggregator, "NewRound"); + }); + + it("returns the new round ID", async () => { + testHelper = await testHelperFactory.connect(personas.Carol).deploy(); + await aggregator.setRequesterPermissions(testHelper.address, true, 0); + let roundId = await testHelper.requestedRoundId(); + assert.equal(roundId.toNumber(), 0); + + await testHelper.requestNewRound(aggregator.address); + + // return value captured by test helper + roundId = await testHelper.requestedRoundId(); + assert.isAbove(roundId.toNumber(), 0); + }); + + describe("when there is a round in progress", () => { + beforeEach(async () => { + await aggregator.requestNewRound(); + }); + + it("reverts", async () => { + await evmRevert(aggregator.requestNewRound(), "prev round must be supersedable"); + }); + + describe("when that round has timed out", () => { + beforeEach(async () => { + await increaseTimeBy(timeout + 1, ethers.provider); + await mineBlock(ethers.provider); + }); + + it("starts a new round", async () => { + await expect(aggregator.requestNewRound()).to.emit(aggregator, "NewRound"); + }); + }); + }); + + describe("when there is a restart delay set", () => { + beforeEach(async () => { + await aggregator.setRequesterPermissions(await personas.Eddy.getAddress(), true, 1); + }); + + it("reverts if a round is started before the delay", async () => { + await aggregator.connect(personas.Eddy).requestNewRound(); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + nextRound = nextRound + 1; + + // Eddy can't start because of the delay + await evmRevert(aggregator.connect(personas.Eddy).requestNewRound(), "must delay requests"); + // Carol starts a new round instead + await aggregator.connect(personas.Carol).requestNewRound(); + + // round completes + await aggregator.connect(personas.Neil).submit(nextRound, answer); + nextRound = nextRound + 1; + + // now Eddy can start again + await aggregator.connect(personas.Eddy).requestNewRound(); + }); + }); + + describe("when all oracles have been removed and then re-added", () => { + it("does not get stuck", async () => { + await aggregator.connect(personas.Carol).changeOracles([await personas.Neil.getAddress()], [], [], 0, 0, 0); + + // advance a few rounds + for (let i = 0; i < 7; i++) { + await aggregator.requestNewRound(); + nextRound = nextRound + 1; + await increaseTimeBy(timeout + 1, ethers.provider); + await mineBlock(ethers.provider); + } + + await addOracles(aggregator, [personas.Neil], 1, 1, 0); + await aggregator.connect(personas.Neil).submit(nextRound, answer); + }); + }); + }); + + describe("#setRequesterPermissions", () => { + beforeEach(async () => { + await addOracles(aggregator, [personas.Neil], 1, 1, 0); + + await aggregator.connect(personas.Neil).submit(nextRound, answer); + nextRound = nextRound + 1; + }); + + describe("when called by the owner", () => { + it("allows the specified address to start new rounds", async () => { + await aggregator.setRequesterPermissions(await personas.Neil.getAddress(), true, 0); + + await aggregator.connect(personas.Neil).requestNewRound(); + }); + + it("emits a log announcing the update", async () => { + await expect(aggregator.setRequesterPermissions(await personas.Neil.getAddress(), true, 0)) + .to.emit(aggregator, "RequesterPermissionsSet") + .withArgs(await personas.Neil.getAddress(), true, 0); + }); + + describe("when the address is already authorized", () => { + beforeEach(async () => { + await aggregator.setRequesterPermissions(await personas.Neil.getAddress(), true, 0); + }); + + it("does not emit a log for already authorized accounts", async () => { + const tx = await aggregator.setRequesterPermissions(await personas.Neil.getAddress(), true, 0); + const receipt = await tx.wait(); + assert.equal(0, receipt?.logs?.length); + }); + }); + + describe("when permission is removed by the owner", () => { + beforeEach(async () => { + await aggregator.setRequesterPermissions(await personas.Neil.getAddress(), true, 0); + }); + + it("does not allow the specified address to start new rounds", async () => { + await aggregator.setRequesterPermissions(await personas.Neil.getAddress(), false, 0); + + await evmRevert(aggregator.connect(personas.Neil).requestNewRound(), "not authorized requester"); + }); + + it("emits a log announcing the update", async () => { + await expect(aggregator.setRequesterPermissions(await personas.Neil.getAddress(), false, 0)) + .to.emit(aggregator, "RequesterPermissionsSet") + .withArgs(await personas.Neil.getAddress(), false, 0); + }); + + it("does not emit a log for accounts without authorization", async () => { + const tx = await aggregator.setRequesterPermissions(await personas.Ned.getAddress(), false, 0); + const receipt = await tx.wait(); + assert.equal(0, receipt?.logs?.length); + }); + }); + }); + + describe("when called by a stranger", () => { + it("reverts", async () => { + await evmRevert( + aggregator.connect(personas.Neil).setRequesterPermissions(await personas.Neil.getAddress(), true, 0), + "Only callable by owner", + ); + + await evmRevert(aggregator.connect(personas.Neil).requestNewRound(), "not authorized requester"); + }); + }); + }); + + describe("#oracleRoundState", () => { + describe("when round ID 0 is passed in", () => { + const previousSubmission = 42; + let baseFunds: any; + let minAnswers: number; + let maxAnswers: number; + let submitters: Signer[]; + + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned, personas.Nelly, personas.Nancy, personas.Norbert]; + minAnswers = 3; + maxAnswers = 4; + + await addOracles(aggregator, oracles, minAnswers, maxAnswers, rrDelay); + submitters = [personas.Nelly, personas.Ned, personas.Neil, personas.Nancy]; + await advanceRound(aggregator, submitters, previousSubmission); + baseFunds = BigNumber.from(deposit).sub(paymentAmount.mul(submitters.length)); + startingState = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + }); + + it("returns all of the important round information", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 2, + latestSubmission: previousSubmission, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds, + oracleCount: oracles.length, + paymentAmount, + }); + }); + + it("reverts if called by a contract", async () => { + testHelper = await testHelperFactory.connect(personas.Carol).deploy(); + await evmRevert( + testHelper.readOracleRoundState(aggregator.address, await personas.Neil.getAddress()), + "off-chain reading only", + ); + }); + + describe("when the restart delay is not enforced", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { + minAnswers, + maxAnswers, + restartDelay: 0, + }); + }); + + describe("< min submissions and oracle not included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil]); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 2, + latestSubmission: previousSubmission, + startedAt: ShouldBeSet, + timeout, + availableFunds: baseFunds.sub(paymentAmount), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe("< min submissions and oracle included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Nelly]); + }); + + it("is not eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 2, + latestSubmission: answer, + startedAt: ShouldBeSet, + timeout, + availableFunds: baseFunds.sub(paymentAmount), + oracleCount: oracles.length, + paymentAmount, + }); + }); + + describe("and timed out", () => { + beforeEach(async () => { + await increaseTimeBy(timeout + 1, ethers.provider); + await mineBlock(ethers.provider); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + describe(">= min submissions and oracle not included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil, personas.Nancy, personas.Ned]); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 2, + latestSubmission: previousSubmission, + startedAt: ShouldBeSet, + timeout, + availableFunds: baseFunds.sub(paymentAmount.mul(3)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe(">= min submissions and oracle included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil, personas.Nelly, personas.Ned]); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(3)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + + describe("and timed out", () => { + beforeEach(async () => { + await increaseTimeBy(timeout + 1, ethers.provider); + await mineBlock(ethers.provider); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(3)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + describe("max submissions and oracle not included", () => { + beforeEach(async () => { + submitters = [personas.Neil, personas.Ned, personas.Nancy, personas.Norbert]; + assert.equal(submitters.length, maxAnswers, "precondition, please update submitters if maxAnswers changes"); + await advanceRound(aggregator, submitters); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 3, + latestSubmission: previousSubmission, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(4)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe("max submissions and oracle included", () => { + beforeEach(async () => { + submitters = [personas.Neil, personas.Ned, personas.Nelly, personas.Nancy]; + assert.equal(submitters.length, maxAnswers, "precondition, please update submitters if maxAnswers changes"); + await advanceRound(aggregator, submitters); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(4)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + describe("when the restart delay is enforced", () => { + beforeEach(async () => { + await updateFutureRounds(aggregator, { + minAnswers, + maxAnswers, + restartDelay: maxAnswers - 1, + }); + }); + + describe("< min submissions and oracle not included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil, personas.Ned]); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 2, + latestSubmission: previousSubmission, + startedAt: ShouldBeSet, + timeout, + availableFunds: baseFunds.sub(paymentAmount.mul(2)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe("< min submissions and oracle included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil, personas.Nelly]); + }); + + it("is not eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 2, + latestSubmission: answer, + startedAt: ShouldBeSet, + timeout, + availableFunds: baseFunds.sub(paymentAmount.mul(2)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + + describe("and timed out", () => { + beforeEach(async () => { + await increaseTimeBy(timeout + 1, ethers.provider); + await mineBlock(ethers.provider); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(2)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + describe(">= min submissions and oracle not included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil, personas.Ned, personas.Nancy]); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 2, + latestSubmission: previousSubmission, + startedAt: ShouldBeSet, + timeout, + availableFunds: baseFunds.sub(paymentAmount.mul(3)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe(">= min submissions and oracle included", () => { + beforeEach(async () => { + await advanceRound(aggregator, [personas.Neil, personas.Ned, personas.Nelly]); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(3)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + + describe("and timed out", () => { + beforeEach(async () => { + await increaseTimeBy(timeout + 1, ethers.provider); + await mineBlock(ethers.provider); + }); + + it("is eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, // restart delay enforced + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(3)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + describe("max submissions and oracle not included", () => { + beforeEach(async () => { + submitters = [personas.Neil, personas.Ned, personas.Nancy, personas.Norbert]; + assert.equal(submitters.length, maxAnswers, "precondition, please update submitters if maxAnswers changes"); + await advanceRound(aggregator, submitters, answer); + }); + + it("is not eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 3, + latestSubmission: previousSubmission, + startedAt: ShouldNotBeSet, + timeout: 0, // details have been deleted + availableFunds: baseFunds.sub(paymentAmount.mul(4)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe("max submissions and oracle included", () => { + beforeEach(async () => { + submitters = [personas.Neil, personas.Ned, personas.Nelly, personas.Nancy]; + assert.equal(submitters.length, maxAnswers, "precondition, please update submitters if maxAnswers changes"); + await advanceRound(aggregator, submitters, answer); + }); + + it("is not eligible to submit", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 3, + latestSubmission: answer, + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: baseFunds.sub(paymentAmount.mul(4)), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + }); + + describe("when non-zero round ID 0 is passed in", () => { + const answers = [0, 42, 47, 52, 57]; + let currentFunds: any; + + beforeEach(async () => { + oracles = [personas.Neil, personas.Ned, personas.Nelly]; + + await addOracles(aggregator, oracles, 2, 3, rrDelay); + startingState = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 0); + await advanceRound(aggregator, oracles, answers[1]); + await advanceRound(aggregator, [personas.Neil, personas.Ned], answers[2]); + await advanceRound(aggregator, oracles, answers[3]); + await advanceRound(aggregator, [personas.Neil], answers[4]); + const submissionsSoFar = 9; + currentFunds = BigNumber.from(deposit).sub(paymentAmount.mul(submissionsSoFar)); + }); + + it("returns info about previous rounds", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 1); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 1, + latestSubmission: answers[3], + startedAt: ShouldBeSet, + timeout: 0, + availableFunds: currentFunds, + oracleCount: oracles.length, + paymentAmount: 0, + }); + }); + + it("returns info about previous rounds that were not submitted to", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 2); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 2, + latestSubmission: answers[3], + startedAt: ShouldBeSet, + timeout, + availableFunds: currentFunds, + oracleCount: oracles.length, + paymentAmount, + }); + }); + + describe("for the current round", () => { + describe("which has not been submitted to", () => { + it("returns info about the current round that hasn't been submitted to", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 4); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 4, + latestSubmission: answers[3], + startedAt: ShouldBeSet, + timeout, + availableFunds: currentFunds, + oracleCount: oracles.length, + paymentAmount, + }); + }); + + it("returns info about the subsequent round", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 5); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 5, + latestSubmission: answers[3], + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: currentFunds, + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + + describe("which has been submitted to", () => { + beforeEach(async () => { + await aggregator.connect(personas.Nelly).submit(4, answers[4]); + }); + + it("returns info about the current round that hasn't been submitted to", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 4); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 4, + latestSubmission: answers[4], + startedAt: ShouldBeSet, + timeout, + availableFunds: currentFunds.sub(paymentAmount), + oracleCount: oracles.length, + paymentAmount, + }); + }); + + it("returns info about the subsequent round", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 5); + + await checkOracleRoundState(state, { + eligibleToSubmit: true, + roundId: 5, + latestSubmission: answers[4], + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: currentFunds.sub(paymentAmount), + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + it("returns speculative info about future rounds", async () => { + const state = await aggregator.oracleRoundState(await personas.Nelly.getAddress(), 6); + + await checkOracleRoundState(state, { + eligibleToSubmit: false, + roundId: 6, + latestSubmission: answers[3], + startedAt: ShouldNotBeSet, + timeout: 0, + availableFunds: currentFunds, + oracleCount: oracles.length, + paymentAmount, + }); + }); + }); + }); + + describe("#getRoundData", () => { + let latestRoundId: any; + beforeEach(async () => { + oracles = [personas.Nelly]; + const minMax = oracles.length; + await addOracles(aggregator, oracles, minMax, minMax, rrDelay); + await advanceRound(aggregator, oracles, answer); + latestRoundId = await aggregator.latestRound(); + }); + + it("returns the relevant round information", async () => { + const round = await aggregator.getRoundData(latestRoundId); + bigNumEquals(latestRoundId, round.roundId); + bigNumEquals(answer, round.answer); + const nowSeconds = new Date().valueOf() / 1000; + assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120); + bigNumEquals(round.updatedAt, round.startedAt); + bigNumEquals(latestRoundId, round.answeredInRound); + }); + + it("reverts if a round is not present", async () => { + await evmRevert(aggregator.getRoundData(latestRoundId.add(1)), "No data present"); + }); + + it("reverts if a round ID is too big", async () => { + const overflowedId = BigNumber.from(2).pow(32).add(1); + + await evmRevert(aggregator.getRoundData(overflowedId), "No data present"); + }); + }); + + describe("#latestRoundData", () => { + beforeEach(async () => { + oracles = [personas.Nelly]; + const minMax = oracles.length; + await addOracles(aggregator, oracles, minMax, minMax, rrDelay); + }); + + describe("when an answer has already been received", () => { + beforeEach(async () => { + await advanceRound(aggregator, oracles, answer); + }); + + it("returns the relevant round info without reverting", async () => { + const round = await aggregator.latestRoundData(); + const latestRoundId = await aggregator.latestRound(); + + bigNumEquals(latestRoundId, round.roundId); + bigNumEquals(answer, round.answer); + const nowSeconds = new Date().valueOf() / 1000; + assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120); + bigNumEquals(round.updatedAt, round.startedAt); + bigNumEquals(latestRoundId, round.answeredInRound); + }); + }); + + it("reverts if a round is not present", async () => { + await evmRevert(aggregator.latestRoundData(), "No data present"); + }); + }); + + describe("#latestAnswer", () => { + beforeEach(async () => { + oracles = [personas.Nelly]; + const minMax = oracles.length; + await addOracles(aggregator, oracles, minMax, minMax, rrDelay); + }); + + describe("when an answer has already been received", () => { + beforeEach(async () => { + await advanceRound(aggregator, oracles, answer); + }); + + it("returns the latest answer without reverting", async () => { + bigNumEquals(answer, await aggregator.latestAnswer()); + }); + }); + + it("returns zero", async () => { + bigNumEquals(0, await aggregator.latestAnswer()); + }); + }); + + describe("#setValidator", () => { + beforeEach(async () => { + validator = await validatorMockFactory.connect(personas.Carol).deploy(); + assert.equal(emptyAddress, await aggregator.validator()); + }); + + it("emits a log event showing the validator was changed", async () => { + await expect(aggregator.connect(personas.Carol).setValidator(validator.address)) + .to.emit(aggregator, "ValidatorUpdated") + .withArgs(emptyAddress, validator.address); + assert.equal(validator.address, await aggregator.validator()); + + await expect(aggregator.connect(personas.Carol).setValidator(validator.address)).to.not.emit( + aggregator, + "ValidatorUpdated", + ); + assert.equal(validator.address, await aggregator.validator()); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert(aggregator.connect(personas.Neil).setValidator(validator.address), "Only callable by owner"); + }); + }); + }); + + describe("integrating with historic deviation checker", () => { + let validator: Contract; + let flags: Contract; + let ac: Contract; + const flaggingThreshold = 1000; // 1% + + beforeEach(async () => { + ac = await acFactory.connect(personas.Carol).deploy(); + flags = await flagsFactory.connect(personas.Carol).deploy(ac.address); + validator = await validatorFactory.connect(personas.Carol).deploy(flags.address, flaggingThreshold); + await ac.connect(personas.Carol).addAccess(validator.address); + + await aggregator.connect(personas.Carol).setValidator(validator.address); + + oracles = [personas.Nelly]; + const minMax = oracles.length; + await addOracles(aggregator, oracles, minMax, minMax, rrDelay); + }); + + it("raises a flag on with high enough deviation", async () => { + await aggregator.connect(personas.Nelly).submit(nextRound, 100); + nextRound++; + + await expect(aggregator.connect(personas.Nelly).submit(nextRound, 102)) + .to.emit(flags, "FlagRaised") + .withArgs(aggregator.address); + }); + + it("does not raise a flag with low enough deviation", async () => { + await aggregator.connect(personas.Nelly).submit(nextRound, 100); + nextRound++; + + await expect(aggregator.connect(personas.Nelly).submit(nextRound, 101)).to.not.emit(flags, "FlagRaised"); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/Median.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/Median.test.ts new file mode 100644 index 0000000..6e2dc3d --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/Median.test.ts @@ -0,0 +1,289 @@ +import { ethers } from "hardhat"; +import { assert } from "chai"; +import { Signer, Contract, ContractFactory, BigNumber } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { bigNumEquals } from "../test-helpers/matchers"; + +let defaultAccount: Signer; +let medianTestHelperFactory: ContractFactory; +before(async () => { + const personas: Personas = (await getUsers()).personas; + defaultAccount = personas.Default; + medianTestHelperFactory = await ethers.getContractFactory( + "src/v0.6/tests/MedianTestHelper.sol:MedianTestHelper", + defaultAccount, + ); +}); + +describe("Median", () => { + let median: Contract; + + beforeEach(async () => { + median = await medianTestHelperFactory.connect(defaultAccount).deploy(); + }); + + describe("testing various lists", () => { + const tests = [ + { + name: "ordered ascending", + responses: [0, 1, 2, 3, 4, 5, 6, 7], + want: 3, + }, + { + name: "ordered descending", + responses: [7, 6, 5, 4, 3, 2, 1, 0], + want: 3, + }, + { + name: "unordered length 1", + responses: [20], + want: 20, + }, + { + name: "unordered length 2", + responses: [20, 0], + want: 10, + }, + { + name: "unordered length 3", + responses: [20, 0, 16], + want: 16, + }, + { + name: "unordered length 4", + responses: [20, 0, 15, 16], + want: 15, + }, + { + name: "unordered length 7", + responses: [1001, 1, 101, 10, 11, 0, 111], + want: 11, + }, + { + name: "unordered length 9", + responses: [8, 8, 4, 5, 5, 7, 9, 5, 9], + want: 7, + }, + { + name: "unordered long", + responses: [33, 44, 89, 101, 67, 7, 23, 55, 88, 324, 0, 88], + want: 61, // 67 + 55 / 2 + }, + { + name: "unordered longer", + responses: [ + 333121, + 323453, + 337654, + 345363, + 345363, + 333456, + 335477, + 333323, + 332352, + 354648, + 983260, + 333856, + 335468, + 376987, + 333253, + 388867, + 337879, + 333324, + 338678, + ], + want: 335477, + }, + { + name: "overflowing numbers", + responses: [ + BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819967"), + BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819967"), + ], + want: BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819967"), + }, + { + name: "overflowing numbers", + responses: [ + BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819967"), + BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819966"), + ], + want: BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819966"), + }, + { + name: "really long", + responses: [ + 56, + 2, + 31, + 33, + 55, + 38, + 35, + 12, + 41, + 47, + 21, + 22, + 40, + 39, + 10, + 32, + 49, + 3, + 54, + 45, + 53, + 14, + 20, + 59, + 1, + 30, + 24, + 6, + 5, + 37, + 58, + 51, + 46, + 17, + 29, + 7, + 27, + 9, + 43, + 8, + 34, + 42, + 28, + 23, + 57, + 0, + 11, + 48, + 52, + 50, + 15, + 16, + 26, + 25, + 4, + 36, + 19, + 44, + 18, + 13, + ], + want: 29, + }, + ]; + + for (const test of tests) { + it(test.name, async () => { + bigNumEquals(test.want, await median.publicGet(test.responses)); + }); + } + }); + + // long running (minutes) exhaustive test. + // skipped because very slow, but useful for thorough validation + xit("permutations", async () => { + const permutations = (list: number[]) => { + const result: number[][] = []; + const used: number[] = []; + + const permute = (unused: number[]) => { + if (unused.length == 0) { + result.push([...used]); + return; + } + + for (let i = 0; i < unused.length; i++) { + const elem = unused.splice(i, 1)[0]; + used.push(elem); + permute(unused); + unused.splice(i, 0, elem); + used.pop(); + } + }; + + permute(list); + return result; + }; + + { + const list = [0, 2, 5, 7, 8, 10]; + for (const permuted of permutations(list)) { + for (let i = 0; i < list.length; i++) { + for (let j = 0; j < list.length; j++) { + if (i < j) { + const foo = await median.publicQuickselectTwo(permuted, i, j); + bigNumEquals(list[i], foo[0]); + bigNumEquals(list[j], foo[1]); + } + } + } + } + } + + { + const list = [0, 1, 1, 1, 2]; + for (const permuted of permutations(list)) { + for (let i = 0; i < list.length; i++) { + for (let j = 0; j < list.length; j++) { + if (i < j) { + const foo = await median.publicQuickselectTwo(permuted, i, j); + bigNumEquals(list[i], foo[0]); + bigNumEquals(list[j], foo[1]); + } + } + } + } + } + }); + + // Checks the validity of the sorting network in `shortList` + describe("validate sorting network", () => { + const net = [ + [0, 1], + [2, 3], + [4, 5], + [0, 2], + [1, 3], + [4, 6], + [1, 2], + [5, 6], + [0, 4], + [1, 5], + [2, 6], + [1, 4], + [3, 6], + [2, 4], + [3, 5], + [3, 4], + ]; + + // See: https://en.wikipedia.org/wiki/Sorting_network#Zero-one_principle + xit("zero-one principle", async () => { + const sortWithNet = (list: number[]) => { + for (const [i, j] of net) { + if (list[i] > list[j]) { + [list[i], list[j]] = [list[j], list[i]]; + } + } + }; + + for (let n = 0; n < (1 << 7) - 1; n++) { + const list = [(n >> 6) & 1, (n >> 5) & 1, (n >> 4) & 1, (n >> 3) & 1, (n >> 2) & 1, (n >> 1) & 1, (n >> 0) & 1]; + const sum = list.reduce((a, b) => a + b, 0); + sortWithNet(list); + const sortedSum = list.reduce((a, b) => a + b, 0); + assert.equal(sortedSum, sum, "Number of zeros and ones changed"); + list.reduce((switched, i) => { + assert.isTrue(!switched || i != 0, "error at n=" + n.toString()); + return i != 0; + }, false); + } + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/Owned.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/Owned.test.ts new file mode 100644 index 0000000..5aba822 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/Owned.test.ts @@ -0,0 +1,72 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Signer, Contract, ContractFactory } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; + +let personas: Personas; + +let owner: Signer; +let nonOwner: Signer; +let newOwner: Signer; + +let ownedFactory: ContractFactory; +let owned: Contract; + +before(async () => { + personas = (await getUsers()).personas; + owner = personas.Carol; + nonOwner = personas.Neil; + newOwner = personas.Ned; + ownedFactory = await ethers.getContractFactory("Owned", owner); +}); + +describe("Owned", () => { + beforeEach(async () => { + owned = await ownedFactory.connect(owner).deploy(); + }); + + it("has a limited public interface", async () => { + publicAbi(owned, ["acceptOwnership", "owner", "transferOwnership"]); + }); + + describe("#constructor", () => { + it("assigns ownership to the deployer", async () => { + const [actual, expected] = await Promise.all([owner.getAddress(), owned.owner()]); + + assert.equal(actual, expected); + }); + }); + + describe("#transferOwnership", () => { + describe("when called by an owner", () => { + it("emits a log", async () => { + await expect(owned.connect(owner).transferOwnership(await newOwner.getAddress())) + .to.emit(owned, "OwnershipTransferRequested") + .withArgs(await owner.getAddress(), await newOwner.getAddress()); + }); + }); + }); + + describe("when called by anyone but the owner", () => { + it("reverts", async () => + await expect(owned.connect(nonOwner).transferOwnership(await newOwner.getAddress())).to.be.reverted); + }); + + describe("#acceptOwnership", () => { + describe("after #transferOwnership has been called", () => { + beforeEach(async () => { + await owned.connect(owner).transferOwnership(await newOwner.getAddress()); + }); + + it("allows the recipient to call it", async () => { + await expect(owned.connect(newOwner).acceptOwnership()) + .to.emit(owned, "OwnershipTransferred") + .withArgs(await owner.getAddress(), await newOwner.getAddress()); + }); + + it("does not allow a non-recipient to call it", async () => + await expect(owned.connect(nonOwner).acceptOwnership()).to.be.reverted); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/PreCoordinator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/PreCoordinator.test.ts new file mode 100644 index 0000000..3b8623a --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/PreCoordinator.test.ts @@ -0,0 +1,632 @@ +import { + contract, + helpers as h, + matchers, + oracle, + setup, +} from '@chainlink/test-helpers' +import cbor from 'cbor' +import { assert } from 'chai' +import { ethers } from 'ethers' +import { BasicConsumer__factory } from '../../ethers/v0.6/factories/BasicConsumer__factory' +import { Oracle__factory } from '../../ethers/v0.6/factories/Oracle__factory' +import { PreCoordinator__factory } from '../../ethers/v0.6/factories/PreCoordinator__factory' + +const provider = setup.provider() +const oracleFactory = new Oracle__factory() +const preCoordinatorFactory = new PreCoordinator__factory() +const requesterConsumerFactory = new BasicConsumer__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +let roles: setup.Roles +beforeAll(async () => { + roles = await setup.users(provider).then((x) => x.roles) +}) + +describe('PreCoordinator', () => { + // These parameters are used to validate the data was received + // on the deployed oracle contract. The Job ID only represents + // the type of data, but will not work on a public testnet. + // For the latest JobIDs, visit our docs here: + // https://docs.chain.link/docs/testnet-oracles + const job1 = + '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' + const job2 = + '0x4c7b7ffb66b344fbaa64995af81e355b00000000000000000000000000000000' + const job3 = + '0x4c7b7ffb66b344fbaa64995af81e355c00000000000000000000000000000000' + const job4 = + '0x4c7b7ffb66b344fbaa64995af81e355d00000000000000000000000000000000' + const currency = 'USD' + + // Represents 1 LINK for testnet requests + const payment = h.toWei('1') + const totalPayment = h.toWei('4') + + let link: contract.Instance + let oc1: contract.Instance + let oc2: contract.Instance + let oc3: contract.Instance + let oc4: contract.Instance + let rc: contract.Instance + let pc: contract.Instance + + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + oc1 = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) + oc2 = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) + oc3 = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) + oc4 = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) + pc = await preCoordinatorFactory + .connect(roles.defaultAccount) + .deploy(link.address) + + await oc1 + .connect(roles.defaultAccount) + .setFulfillmentPermission(roles.oracleNode1.address, true) + await oc2 + .connect(roles.defaultAccount) + .setFulfillmentPermission(roles.oracleNode2.address, true) + await oc3 + .connect(roles.defaultAccount) + .setFulfillmentPermission(roles.oracleNode3.address, true) + await oc4 + .connect(roles.defaultAccount) + .setFulfillmentPermission(roles.oracleNode4.address, true) + }) + + beforeEach(deployment) + + describe('#createServiceAgreement', () => { + it('emits the NewServiceAgreement log', async () => { + const tx = await pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 3, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ) + const receipt = await tx.wait() + + expect( + h.findEventIn(receipt, pc.interface.events.NewServiceAgreement), + ).toBeDefined() + }) + + it('creates a service agreement', async () => { + const tx = await pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 3, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ) + const receipt = await tx.wait() + const { saId } = h.eventArgs( + h.findEventIn(receipt, pc.interface.events.NewServiceAgreement), + ) + + const sa = await pc.getServiceAgreement(saId) + assert.isTrue(sa.totalPayment.eq(totalPayment)) + assert.equal(sa.minResponses.toNumber(), 3) + assert.deepEqual(sa.oracles, [ + oc1.address, + oc2.address, + oc3.address, + oc4.address, + ]) + assert.deepEqual(sa.jobIds, [job1, job2, job3, job4]) + assert.deepEqual( + sa.payments.map((p) => p.toHexString()), + [payment, payment, payment, payment].map((p) => p.toHexString()), + ) + }) + + it('does not allow service agreements with 0 minResponses', () => + matchers.evmRevert( + pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 0, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ), + 'Min responses must be > 0', + )) + + describe('when the array lengths are not equal', () => { + it('reverts', () => + matchers.evmRevert( + pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 3, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3], + [payment, payment, payment, payment], + ), + 'Unmet length', + )) + }) + + describe('when the min responses is greater than the oracles', () => { + it('reverts', () => + matchers.evmRevert( + pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 5, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ), + 'Invalid min responses', + )) + }) + }) + + describe('#onTokenTransfer', () => { + describe('when called by an address other than the LINK token', () => { + it('reverts', async () => { + const notLink = await linkTokenFactory + .connect(roles.defaultAccount) + .deploy() + + const tx = await pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 3, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ) + const receipt = await tx.wait() + const saId = h.eventArgs( + h.findEventIn(receipt, pc.interface.events.NewServiceAgreement), + ).saId + + const badRc = await requesterConsumerFactory + .connect(roles.consumer) + .deploy(notLink.address, pc.address, saId) + + await notLink + .connect(roles.defaultAccount) + .transfer(badRc.address, totalPayment) + + await matchers.evmRevert( + badRc + .connect(roles.consumer) + .requestEthereumPrice(currency, totalPayment, {}), + ) + }) + }) + + describe('when called by the LINK token', () => { + let saId: string + beforeEach(async () => { + const tx = await pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 3, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ) + const receipt = await tx.wait() + saId = h.eventArgs( + h.findEventIn(receipt, pc.interface.events.NewServiceAgreement), + ).saId + + rc = await requesterConsumerFactory + .connect(roles.consumer) + .deploy(link.address, pc.address, saId) + await link.transfer(rc.address, totalPayment) + }) + + it('creates Chainlink requests', async () => { + const tx = await rc + .connect(roles.consumer) + .requestEthereumPrice(currency, totalPayment) + const receipt = await tx.wait() + + const log1 = receipt.logs?.[7] + assert.equal(oc1.address, log1?.address) + const request1 = oracle.decodeRunRequest(log1) + assert.equal(request1.requester, pc.address) + const log2 = receipt.logs?.[11] + assert.equal(oc2.address, log2?.address) + const request2 = oracle.decodeRunRequest(log2) + assert.equal(request2.requester, pc.address) + const log3 = receipt.logs?.[15] + assert.equal(oc3.address, log3?.address) + const request3 = oracle.decodeRunRequest(log3) + assert.equal(request3.requester, pc.address) + const log4 = receipt.logs?.[19] + assert.equal(oc4.address, log4?.address) + const request4 = oracle.decodeRunRequest(log4) + assert.equal(request4.requester, pc.address) + const expected = { + path: ['USD'], + get: + 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY', + } + assert.deepEqual(expected, await cbor.decodeFirst(request1.data)) + assert.deepEqual(expected, await cbor.decodeFirst(request2.data)) + assert.deepEqual(expected, await cbor.decodeFirst(request3.data)) + assert.deepEqual(expected, await cbor.decodeFirst(request4.data)) + }) + + describe('when insufficient payment is supplied', () => { + it('reverts', () => + matchers.evmRevert( + rc.connect(roles.consumer).requestEthereumPrice(currency, payment), + )) + }) + + describe('when the same nonce is used twice', () => { + const nonce = 1 + const fHash = '0xabcd1234' + let args: string + beforeEach(async () => { + args = oracle.encodeOracleRequest( + saId, + rc.address, + fHash, + nonce, + '0x0', + ) + await link.transferAndCall(pc.address, totalPayment, args) + }) + + it('reverts', () => + matchers.evmRevert( + link.transferAndCall(pc.address, totalPayment, args), + )) + }) + + describe('when too much payment is supplied', () => { + it('sends the extra back to the requester', async () => { + await link.transfer(rc.address, payment) + const extraPayment = h.toWei('5') + const beforeBalance = await link.balanceOf(rc.address) + expect(beforeBalance.eq(extraPayment)).toBeTruthy() + + await rc + .connect(roles.consumer) + .requestEthereumPrice(currency, extraPayment) + const afterBalance = await link.balanceOf(rc.address) + expect(afterBalance.eq(payment)).toBeTruthy() + }) + }) + }) + }) + + describe('#chainlinkCallback', () => { + let saId: string + let request1: oracle.RunRequest + let request2: oracle.RunRequest + let request3: oracle.RunRequest + let request4: oracle.RunRequest + const response1 = h.numToBytes32(100) + const response2 = h.numToBytes32(101) + const response3 = h.numToBytes32(102) + const response4 = h.numToBytes32(103) + + beforeEach(async () => { + const tx = await pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 2, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ) + const receipt = await tx.wait() + + saId = h.eventArgs( + h.findEventIn(receipt, pc.interface.events.NewServiceAgreement), + ).saId + rc = await requesterConsumerFactory + .connect(roles.consumer) + .deploy(link.address, pc.address, saId) + await link.transfer(rc.address, totalPayment) + }) + + describe('when the requester and consumer are the same', () => { + beforeEach(async () => { + const reqTx = await rc + .connect(roles.consumer) + .requestEthereumPrice(currency, totalPayment) + const receipt = await reqTx.wait() + + const log1 = receipt.logs?.[7] + request1 = oracle.decodeRunRequest(log1) + const log2 = receipt.logs?.[11] + request2 = oracle.decodeRunRequest(log2) + const log3 = receipt.logs?.[15] + request3 = oracle.decodeRunRequest(log3) + const log4 = receipt.logs?.[19] + request4 = oracle.decodeRunRequest(log4) + }) + + describe('when called by a stranger', () => { + it('reverts', () => + matchers.evmRevert( + pc.chainlinkCallback(saId, response1), + 'Source must be the oracle of the request', + )) + }) + + describe('when called by the oracle contract', () => { + it('records the answer', async () => { + const tx = await oc1 + .connect(roles.oracleNode1) + .fulfillOracleRequest( + request1.requestId, + request1.payment, + request1.callbackAddr, + request1.callbackFunc, + request1.expiration, + response1, + ) + const receipt = await tx.wait() + + expect( + receipt.events?.[0].topics.find( + (t) => t === pc.interface.events.ChainlinkFulfilled.topic, + ), + ).toBeDefined() + }) + }) + + describe('when the minimum number of responses have returned', () => { + beforeEach(async () => { + await oc1 + .connect(roles.oracleNode1) + .fulfillOracleRequest( + request1.requestId, + request1.payment, + request1.callbackAddr, + request1.callbackFunc, + request1.expiration, + response1, + ) + await oc2 + .connect(roles.oracleNode2) + .fulfillOracleRequest( + request2.requestId, + request2.payment, + request2.callbackAddr, + request2.callbackFunc, + request2.expiration, + response2, + ) + await oc3 + .connect(roles.oracleNode3) + .fulfillOracleRequest( + request3.requestId, + request3.payment, + request3.callbackAddr, + request3.callbackFunc, + request3.expiration, + response3, + ) + }) + + it('returns the median to the requesting contract', async () => { + const currentPrice = await rc.currentPrice() + assert.equal(currentPrice, response1) + }) + + describe('when an oracle responds after aggregation', () => { + it('does not update the requesting contract', async () => { + await oc4 + .connect(roles.oracleNode4) + .fulfillOracleRequest( + request4.requestId, + request4.payment, + request4.callbackAddr, + request4.callbackFunc, + request4.expiration, + response4, + ) + const currentPrice = await rc.currentPrice() + assert.equal(currentPrice, response1) + }) + }) + }) + }) + + describe('when consumer is different than requester', () => { + let cc: contract.Instance + let request1: oracle.RunRequest + let request2: oracle.RunRequest + let request3: oracle.RunRequest + let request4: oracle.RunRequest + let localRequestId: string + + beforeEach(async () => { + cc = await requesterConsumerFactory + .connect(roles.consumer) + .deploy(link.address, pc.address, saId) + const reqTx = await rc + .connect(roles.consumer) + .requestEthereumPriceByCallback(currency, totalPayment, cc.address) + const receipt = await reqTx.wait() + + localRequestId = h.eventArgs(receipt.events?.[0]).id + const log1 = receipt.logs?.[7] + request1 = oracle.decodeRunRequest(log1) + const log2 = receipt.logs?.[11] + request2 = oracle.decodeRunRequest(log2) + const log3 = receipt.logs?.[15] + request3 = oracle.decodeRunRequest(log3) + const log4 = receipt.logs?.[19] + request4 = oracle.decodeRunRequest(log4) + + await cc + .connect(roles.consumer) + .addExternalRequest(pc.address, localRequestId) + }) + + describe('and the number of responses have been met', () => { + beforeEach(async () => { + await oc1 + .connect(roles.oracleNode1) + .fulfillOracleRequest( + request1.requestId, + request1.payment, + request1.callbackAddr, + request1.callbackFunc, + request1.expiration, + response1, + ) + await oc2 + .connect(roles.oracleNode2) + .fulfillOracleRequest( + request2.requestId, + request2.payment, + request2.callbackAddr, + request2.callbackFunc, + request2.expiration, + response2, + ) + await oc3 + .connect(roles.oracleNode3) + .fulfillOracleRequest( + request3.requestId, + request3.payment, + request3.callbackAddr, + request3.callbackFunc, + request3.expiration, + response3, + ) + await oc4 + .connect(roles.oracleNode4) + .fulfillOracleRequest( + request4.requestId, + request4.payment, + request4.callbackAddr, + request4.callbackFunc, + request4.expiration, + response4, + ) + }) + + it('sends the answer to the consumer', async () => { + const currentPrice = await cc.currentPrice() + assert.equal(currentPrice, response1) + }) + }) + }) + }) + + describe('#withdrawLink', () => { + beforeEach(async () => { + await link.transfer(pc.address, payment) + + const actual = await link.balanceOf(pc.address) + const expected = payment + expect(actual.eq(expected)).toBeTruthy() + }) + + describe('when called by a stranger', () => { + it('reverts', () => + matchers.evmRevert(pc.connect(roles.stranger).withdrawLink())) + }) + + describe('when called by the owner', () => { + it('allows the owner to withdraw LINK', async () => { + await pc.connect(roles.defaultAccount).withdrawLink() + + const actual = await link.balanceOf(pc.address) + expect(actual.eq(ethers.constants.Zero)).toBeTruthy() + }) + }) + }) + + describe('#cancelOracleRequest', () => { + let request: oracle.RunRequest + + beforeEach(async () => { + const tx = await pc + .connect(roles.defaultAccount) + .createServiceAgreement( + 3, + [oc1.address, oc2.address, oc3.address, oc4.address], + [job1, job2, job3, job4], + [payment, payment, payment, payment], + ) + const receipt = await tx.wait() + + const saId = h.eventArgs( + h.findEventIn(receipt, pc.interface.events.NewServiceAgreement), + ).saId + + rc = await requesterConsumerFactory + .connect(roles.consumer) + .deploy(link.address, pc.address, saId) + await link.transfer(rc.address, totalPayment) + + const reqTx = await rc + .connect(roles.consumer) + .requestEthereumPrice(currency, totalPayment) + const reqReceipt = await reqTx.wait() + + const log1 = reqReceipt.logs?.[7] + request = oracle.decodeRunRequest(log1) + }) + + describe('before the minimum required time', () => { + it('does not allow requests to be cancelled', () => + matchers.evmRevert( + rc + .connect(roles.consumer) + .cancelRequest( + pc.address, + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ), + 'Request is not expired', + )) + }) + + describe('after the minimum required time', () => { + beforeEach(async () => { + await h.increaseTime5Minutes(provider) + }) + + it('allows the requester to cancel', async () => { + await rc + .connect(roles.consumer) + .cancelRequest( + pc.address, + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ) + const balance = await link.balanceOf(rc.address) + expect(balance.eq(payment)).toBeTruthy() + }) + + it('does not allow others to call', () => + matchers.evmRevert( + pc + .connect(roles.stranger) + .cancelOracleRequest( + request.requestId, + request.payment, + request.callbackFunc, + request.expiration, + ), + 'Only requester can cancel', + )) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/SignedSafeMath.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/SignedSafeMath.test.ts new file mode 100644 index 0000000..ede7870 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/SignedSafeMath.test.ts @@ -0,0 +1,176 @@ +import { ethers } from "hardhat"; +import { expect } from "chai"; +import { Signer, Contract, ContractFactory, BigNumber } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { bigNumEquals } from "../test-helpers/matchers"; + +let defaultAccount: Signer; +let concreteSignedSafeMathFactory: ContractFactory; + +before(async () => { + const personas: Personas = (await getUsers()).personas; + defaultAccount = personas.Default; + concreteSignedSafeMathFactory = await ethers.getContractFactory("ConcreteSignedSafeMath", defaultAccount); +}); + +describe("SignedSafeMath", () => { + // a version of the adder contract where we make all ABI exposed functions constant + // TODO: submit upstream PR to support constant contract type generation + let adder: Contract; + let response: BigNumber; + + const INT256_MAX = BigNumber.from("57896044618658097711785492504343953926634992332820282019728792003956564819967"); + const INT256_MIN = BigNumber.from("-57896044618658097711785492504343953926634992332820282019728792003956564819968"); + + beforeEach(async () => { + adder = await concreteSignedSafeMathFactory.connect(defaultAccount).deploy(); + }); + + describe("#add", () => { + describe("given a positive and a positive", () => { + it("works", async () => { + response = await adder.testAdd(1, 2); + bigNumEquals(3, response); + }); + + it("works with zero", async () => { + response = await adder.testAdd(INT256_MAX, 0); + bigNumEquals(INT256_MAX, response); + }); + + describe("when both are large enough to overflow", () => { + it("throws", async () => { + await expect(adder.testAdd(INT256_MAX, 1)).to.be.revertedWith("SignedSafeMath: addition overflow"); + }); + }); + }); + + describe("given a negative and a negative", () => { + it("works", async () => { + response = await adder.testAdd(-1, -2); + bigNumEquals(-3, response); + }); + + it("works with zero", async () => { + response = await adder.testAdd(INT256_MIN, 0); + bigNumEquals(INT256_MIN, response); + }); + + describe("when both are large enough to overflow", () => { + it("throws", async () => { + await expect(adder.testAdd(INT256_MIN, -1)).to.be.revertedWith("SignedSafeMath: addition overflow"); + }); + }); + }); + + describe("given a positive and a negative", () => { + it("works", async () => { + response = await adder.testAdd(1, -2); + bigNumEquals(-1, response); + }); + }); + + describe("given a negative and a positive", () => { + it("works", async () => { + response = await adder.testAdd(-1, 2); + bigNumEquals(1, response); + }); + }); + }); + + describe("#avg", () => { + describe("given a positive and a positive", () => { + it("works", async () => { + response = await adder.testAvg(2, 4); + bigNumEquals(3, response); + }); + + it("works with zero", async () => { + response = await adder.testAvg(0, 4); + bigNumEquals(2, response); + response = await adder.testAvg(4, 0); + bigNumEquals(2, response); + }); + + it("works with large numbers", async () => { + response = await adder.testAvg(INT256_MAX, INT256_MAX); + bigNumEquals(INT256_MAX, response); + }); + + it("rounds towards zero", async () => { + response = await adder.testAvg(1, 2); + bigNumEquals(1, response); + }); + }); + + describe("given a negative and a negative", () => { + it("works", async () => { + response = await adder.testAvg(-2, -4); + bigNumEquals(-3, response); + }); + + it("works with zero", async () => { + response = await adder.testAvg(0, -4); + bigNumEquals(-2, response); + response = await adder.testAvg(-4, 0); + bigNumEquals(-2, response); + }); + + it("works with large numbers", async () => { + response = await adder.testAvg(INT256_MIN, INT256_MIN); + bigNumEquals(INT256_MIN, response); + }); + + it("rounds towards zero", async () => { + response = await adder.testAvg(-1, -2); + bigNumEquals(-1, response); + }); + }); + + describe("given a positive and a negative", () => { + it("works", async () => { + response = await adder.testAvg(2, -4); + bigNumEquals(-1, response); + response = await adder.testAvg(4, -2); + bigNumEquals(1, response); + }); + + it("works with large numbers", async () => { + response = await adder.testAvg(INT256_MAX, -2); + bigNumEquals(INT256_MAX.sub(2).div(2), response); + response = await adder.testAvg(INT256_MAX, INT256_MIN); + bigNumEquals(0, response); + }); + + it("rounds towards zero", async () => { + response = await adder.testAvg(1, -4); + bigNumEquals(-1, response); + response = await adder.testAvg(4, -1); + bigNumEquals(1, response); + }); + }); + + describe("given a negative and a positive", () => { + it("works", async () => { + response = await adder.testAvg(-2, 4); + bigNumEquals(1, response); + response = await adder.testAvg(-4, 2); + bigNumEquals(-1, response); + }); + + it("works with large numbers", async () => { + response = await adder.testAvg(INT256_MIN, 2); + bigNumEquals(INT256_MIN.add(2).div(2), response); + response = await adder.testAvg(INT256_MIN, INT256_MAX); + bigNumEquals(0, response); + }); + + it("rounds towards zero", async () => { + response = await adder.testAvg(-1, 4); + bigNumEquals(1, response); + response = await adder.testAvg(-4, 1); + bigNumEquals(-1, response); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/SimpleReadAccessController.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/SimpleReadAccessController.test.ts new file mode 100644 index 0000000..68111cd --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/SimpleReadAccessController.test.ts @@ -0,0 +1,211 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Contract, ContractFactory, Transaction } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; + +let personas: Personas; + +let controllerFactory: ContractFactory; +let controller: Contract; + +before(async () => { + personas = (await getUsers()).personas; + controllerFactory = await ethers.getContractFactory("SimpleReadAccessController", personas.Carol); +}); + +describe("SimpleReadAccessController", () => { + beforeEach(async () => { + controller = await controllerFactory.connect(personas.Carol).deploy(); + }); + + it("has a limited public interface", async () => { + publicAbi(controller, [ + "hasAccess", + "addAccess", + "disableAccessCheck", + "enableAccessCheck", + "removeAccess", + "checkEnabled", + // Owned + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#constructor", () => { + it("defaults checkEnabled to true", async () => { + assert(await controller.checkEnabled()); + }); + }); + + describe("#hasAccess", () => { + it("allows unauthorized calls originating from the same account", async () => { + assert.isTrue(await controller.connect(personas.Eddy).hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("blocks unauthorized calls originating from different accounts", async () => { + assert.isFalse(await controller.connect(personas.Carol).hasAccess(await personas.Eddy.getAddress(), "0x00")); + assert.isFalse(await controller.connect(personas.Eddy).hasAccess(await personas.Carol.getAddress(), "0x00")); + }); + }); + + describe("#addAccess", () => { + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(controller.connect(personas.Eddy).addAccess(await personas.Eddy.getAddress())).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + assert.isFalse(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + tx = await controller.addAccess(await personas.Eddy.getAddress()); + }); + + it("adds the address to the controller", async () => { + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + await expect(tx) + .to.emit(controller, "AddedAccess") + .withArgs(await personas.Eddy.getAddress()); + }); + + describe("when called twice", () => { + it("does not emit a log", async () => { + const tx2 = await controller.addAccess(await personas.Eddy.getAddress()); + const receipt = await tx2.wait(); + assert.equal(receipt.events?.length, 0); + }); + }); + }); + }); + + describe("#removeAccess", () => { + beforeEach(async () => { + await controller.addAccess(await personas.Eddy.getAddress()); + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect( + controller.connect(personas.Eddy).removeAccess(await personas.Eddy.getAddress()), + ).to.be.revertedWith("Only callable by owner"); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + tx = await controller.removeAccess(await personas.Eddy.getAddress()); + }); + + it("removes the address from the controller", async () => { + assert.isFalse(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + await expect(tx) + .to.emit(controller, "RemovedAccess") + .withArgs(await personas.Eddy.getAddress()); + }); + + describe("when called twice", () => { + it("does not emit a log", async () => { + const tx2 = await controller.removeAccess(await personas.Eddy.getAddress()); + const receipt = await tx2.wait(); + assert.equal(receipt.events?.length, 0); + }); + }); + }); + }); + + describe("#disableAccessCheck", () => { + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(controller.connect(personas.Eddy).disableAccessCheck()).to.be.revertedWith( + "Only callable by owner", + ); + assert.isTrue(await controller.checkEnabled()); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + await controller.addAccess(await personas.Eddy.getAddress()); + tx = await controller.disableAccessCheck(); + }); + + it("sets checkEnabled to false", async () => { + assert.isFalse(await controller.checkEnabled()); + }); + + it("allows users with access", async () => { + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("allows users without access", async () => { + assert.isTrue(await controller.hasAccess(await personas.Ned.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + await expect(tx).to.emit(controller, "CheckAccessDisabled"); + }); + + describe("when called twice", () => { + it("does not emit a log", async () => { + const tx2 = await controller.disableAccessCheck(); + const receipt = await tx2.wait(); + assert.equal(receipt.events?.length, 0); + }); + }); + }); + }); + + describe("#enableAccessCheck", () => { + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(controller.connect(personas.Eddy).enableAccessCheck()).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + await controller.disableAccessCheck(); + await controller.addAccess(await personas.Eddy.getAddress()); + tx = await controller.enableAccessCheck(); + }); + + it("allows users with access", async () => { + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("does not allow users without access", async () => { + assert.isFalse(await controller.hasAccess(await personas.Ned.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + expect(tx).to.emit(controller, "CheckAccessEnabled"); + }); + + describe("when called twice", () => { + it("does not emit a log", async () => { + const tx2 = await controller.enableAccessCheck(); + const receipt = await tx2.wait(); + assert.equal(receipt.events?.length, 0); + }); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/SimpleWriteAccessController.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/SimpleWriteAccessController.test.ts new file mode 100644 index 0000000..4275e94 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/SimpleWriteAccessController.test.ts @@ -0,0 +1,179 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Contract, ContractFactory, Transaction } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; + +let personas: Personas; + +let controllerFactory: ContractFactory; +let controller: Contract; + +before(async () => { + personas = (await getUsers()).personas; + controllerFactory = await ethers.getContractFactory("SimpleWriteAccessController", personas.Carol); +}); + +describe("SimpleWriteAccessController", () => { + beforeEach(async () => { + controller = await controllerFactory.connect(personas.Carol).deploy(); + }); + + it("has a limited public interface", async () => { + publicAbi(controller, [ + "hasAccess", + "addAccess", + "disableAccessCheck", + "enableAccessCheck", + "removeAccess", + "checkEnabled", + // Owned + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#constructor", () => { + it("defaults checkEnabled to true", async () => { + assert(await controller.checkEnabled()); + }); + }); + + describe("#hasAccess", () => { + it("allows unauthorized calls originating from the same account", async () => { + assert.isFalse(await controller.connect(personas.Eddy).hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("blocks unauthorized calls originating from different accounts", async () => { + assert.isFalse(await controller.connect(personas.Carol).hasAccess(await personas.Eddy.getAddress(), "0x00")); + assert.isFalse(await controller.connect(personas.Eddy).hasAccess(await personas.Carol.getAddress(), "0x00")); + }); + }); + + describe("#addAccess", () => { + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(controller.connect(personas.Eddy).addAccess(await personas.Eddy.getAddress())).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + assert.isFalse(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + tx = await controller.addAccess(await personas.Eddy.getAddress()); + }); + + it("adds the address to the controller", async () => { + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + expect(tx) + .to.emit(controller, "AddedAccess") + .withArgs(await personas.Eddy.getAddress()); + }); + }); + }); + + describe("#removeAccess", () => { + beforeEach(async () => { + await controller.addAccess(await personas.Eddy.getAddress()); + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect( + controller.connect(personas.Eddy).removeAccess(await personas.Eddy.getAddress()), + ).to.be.revertedWith("Only callable by owner"); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + tx = await controller.removeAccess(await personas.Eddy.getAddress()); + }); + + it("removes the address from the controller", async () => { + assert.isFalse(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + expect(tx) + .to.emit(controller, "RemovedAccess") + .withArgs(await personas.Eddy.getAddress()); + }); + }); + }); + + describe("#disableAccessCheck", () => { + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(controller.connect(personas.Eddy).disableAccessCheck()).to.be.revertedWith( + "Only callable by owner", + ); + assert.isTrue(await controller.checkEnabled()); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + await controller.addAccess(await personas.Eddy.getAddress()); + tx = await controller.disableAccessCheck(); + }); + + it("sets checkEnabled to false", async () => { + assert.isFalse(await controller.checkEnabled()); + }); + + it("allows users with access", async () => { + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("allows users without access", async () => { + assert.isTrue(await controller.hasAccess(await personas.Ned.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + await expect(tx).to.emit(controller, "CheckAccessDisabled"); + }); + }); + }); + + describe("#enableAccessCheck", () => { + describe("when called by a non-owner", () => { + it("reverts", async () => { + await expect(controller.connect(personas.Eddy).enableAccessCheck()).to.be.revertedWith( + "Only callable by owner", + ); + }); + }); + + describe("when called by the owner", () => { + let tx: Transaction; + beforeEach(async () => { + await controller.disableAccessCheck(); + await controller.addAccess(await personas.Eddy.getAddress()); + tx = await controller.enableAccessCheck(); + }); + + it("allows users with access", async () => { + assert.isTrue(await controller.hasAccess(await personas.Eddy.getAddress(), "0x00")); + }); + + it("does not allow users without access", async () => { + assert.isFalse(await controller.hasAccess(await personas.Ned.getAddress(), "0x00")); + }); + + it("announces the change via a log", async () => { + await expect(tx).to.emit(controller, "CheckAccessEnabled"); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/VRFD20.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/VRFD20.test.ts new file mode 100644 index 0000000..0472873 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/VRFD20.test.ts @@ -0,0 +1,243 @@ +import { ethers } from "hardhat"; +import { assert, expect } from "chai"; +import { BigNumber, constants, Contract, ContractFactory, ContractTransaction } from "ethers"; +import { getUsers, Personas, Roles } from "../test-helpers/setup"; +import { + evmWordToAddress, + getLog, + publicAbi, + toBytes32String, + toWei, + numToBytes32, + getLogs, +} from "../test-helpers/helpers"; + +let roles: Roles; +let personas: Personas; +let linkTokenFactory: ContractFactory; +let vrfCoordinatorMockFactory: ContractFactory; +let vrfD20Factory: ContractFactory; + +before(async () => { + const users = await getUsers(); + + roles = users.roles; + personas = users.personas; + linkTokenFactory = await ethers.getContractFactory("LinkToken", roles.defaultAccount); + vrfCoordinatorMockFactory = await ethers.getContractFactory("VRFCoordinatorMock", roles.defaultAccount); + vrfD20Factory = await ethers.getContractFactory("VRFD20", roles.defaultAccount); +}); + +describe("VRFD20", () => { + const deposit = toWei("1"); + const fee = toWei("0.1"); + const keyHash = toBytes32String("keyHash"); + + let link: Contract; + let vrfCoordinator: Contract; + let vrfD20: Contract; + + beforeEach(async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy(); + vrfCoordinator = await vrfCoordinatorMockFactory.connect(roles.defaultAccount).deploy(link.address); + vrfD20 = await vrfD20Factory + .connect(roles.defaultAccount) + .deploy(vrfCoordinator.address, link.address, keyHash, fee); + await link.transfer(vrfD20.address, deposit); + }); + + it("has a limited public interface", () => { + publicAbi(vrfD20, [ + // Owned + "acceptOwnership", + "owner", + "transferOwnership", + //VRFConsumerBase + "rawFulfillRandomness", + // VRFD20 + "rollDice", + "house", + "withdrawLINK", + "keyHash", + "fee", + "setKeyHash", + "setFee", + ]); + }); + + describe("#withdrawLINK", () => { + describe("failure", () => { + it("reverts when called by a non-owner", async () => { + await expect( + vrfD20.connect(roles.stranger).withdrawLINK(await roles.stranger.getAddress(), deposit), + ).to.be.revertedWith("Only callable by owner"); + }); + + it("reverts when not enough LINK in the contract", async () => { + const withdrawAmount = deposit.mul(2); + await expect( + vrfD20.connect(roles.defaultAccount).withdrawLINK(await roles.defaultAccount.getAddress(), withdrawAmount), + ).to.be.reverted; + }); + }); + + describe("success", () => { + it("withdraws LINK", async () => { + const startingAmount = await link.balanceOf(await roles.defaultAccount.getAddress()); + const expectedAmount = BigNumber.from(startingAmount).add(deposit); + await vrfD20.connect(roles.defaultAccount).withdrawLINK(await roles.defaultAccount.getAddress(), deposit); + const actualAmount = await link.balanceOf(await roles.defaultAccount.getAddress()); + assert.equal(actualAmount.toString(), expectedAmount.toString()); + }); + }); + }); + + describe("#setKeyHash", () => { + const newHash = toBytes32String("newhash"); + + describe("failure", () => { + it("reverts when called by a non-owner", async () => { + await expect(vrfD20.connect(roles.stranger).setKeyHash(newHash)).to.be.revertedWith("Only callable by owner"); + }); + }); + + describe("success", () => { + it("sets the key hash", async () => { + await vrfD20.setKeyHash(newHash); + const actualHash = await vrfD20.keyHash(); + assert.equal(actualHash, newHash); + }); + }); + }); + + describe("#setFee", () => { + const newFee = 1234; + + describe("failure", () => { + it("reverts when called by a non-owner", async () => { + await expect(vrfD20.connect(roles.stranger).setFee(newFee)).to.be.revertedWith("Only callable by owner"); + }); + }); + + describe("success", () => { + it("sets the fee", async () => { + await vrfD20.setFee(newFee); + const actualFee = await vrfD20.fee(); + assert.equal(actualFee.toString(), newFee.toString()); + }); + }); + }); + + describe("#house", () => { + describe("failure", () => { + it("reverts when dice not rolled", async () => { + await expect(vrfD20.house(await personas.Nancy.getAddress())).to.be.revertedWith("Dice not rolled"); + }); + + it("reverts when dice roll is in progress", async () => { + await vrfD20.rollDice(await personas.Nancy.getAddress()); + await expect(vrfD20.house(await personas.Nancy.getAddress())).to.be.revertedWith("Roll in progress"); + }); + }); + + describe("success", () => { + it("returns the correct house", async () => { + const randomness = 98765; + const expectedHouse = "Martell"; + const tx = await vrfD20.rollDice(await personas.Nancy.getAddress()); + const log = await getLog(tx, 3); + const eventRequestId = log?.topics?.[1]; + await vrfCoordinator.callBackWithRandomness(eventRequestId, randomness, vrfD20.address); + const response = await vrfD20.house(await personas.Nancy.getAddress()); + assert.equal(response.toString(), expectedHouse); + }); + }); + }); + + describe("#rollDice", () => { + describe("success", () => { + let tx: ContractTransaction; + beforeEach(async () => { + tx = await vrfD20.rollDice(await personas.Nancy.getAddress()); + }); + + it("emits a RandomnessRequest event from the VRFCoordinator", async () => { + const log = await getLog(tx, 2); + const topics = log?.topics; + assert.equal(evmWordToAddress(topics?.[1]), vrfD20.address); + assert.equal(topics?.[2], keyHash); + assert.equal(topics?.[3], constants.HashZero); + }); + }); + + describe("failure", () => { + it("reverts when LINK balance is zero", async () => { + const vrfD202 = await vrfD20Factory + .connect(roles.defaultAccount) + .deploy(vrfCoordinator.address, link.address, keyHash, fee); + await expect(vrfD202.rollDice(await personas.Nancy.getAddress())).to.be.revertedWith( + "Not enough LINK to pay fee", + ); + }); + + it("reverts when called by a non-owner", async () => { + await expect(vrfD20.connect(roles.stranger).rollDice(await personas.Nancy.getAddress())).to.be.revertedWith( + "Only callable by owner", + ); + }); + + it("reverts when the roller rolls more than once", async () => { + await vrfD20.rollDice(await personas.Nancy.getAddress()); + await expect(vrfD20.rollDice(await personas.Nancy.getAddress())).to.be.revertedWith("Already rolled"); + }); + }); + }); + + describe("#fulfillRandomness", () => { + const randomness = 98765; + const expectedModResult = (randomness % 20) + 1; + const expectedHouse = "Martell"; + let eventRequestId: string; + beforeEach(async () => { + const tx = await vrfD20.rollDice(await personas.Nancy.getAddress()); + const log = await getLog(tx, 3); + eventRequestId = log?.topics?.[1]; + }); + + describe("success", () => { + let tx: ContractTransaction; + beforeEach(async () => { + tx = await vrfCoordinator.callBackWithRandomness(eventRequestId, randomness, vrfD20.address); + }); + + it("emits a DiceLanded event", async () => { + const log = await getLog(tx, 0); + assert.equal(log?.topics[1], eventRequestId); + assert.equal(log?.topics[2], numToBytes32(expectedModResult)); + }); + + it("sets the correct dice roll result", async () => { + const response = await vrfD20.house(await personas.Nancy.getAddress()); + assert.equal(response.toString(), expectedHouse); + }); + + it("allows someone else to roll", async () => { + const secondRandomness = 55555; + tx = await vrfD20.rollDice(await personas.Ned.getAddress()); + const log = await getLog(tx, 3); + eventRequestId = log?.topics?.[1]; + tx = await vrfCoordinator.callBackWithRandomness(eventRequestId, secondRandomness, vrfD20.address); + }); + }); + + describe("failure", () => { + it("does not fulfill when fulfilled by the wrong VRFcoordinator", async () => { + const vrfCoordinator2 = await vrfCoordinatorMockFactory.connect(roles.defaultAccount).deploy(link.address); + + const tx = await vrfCoordinator2.callBackWithRandomness(eventRequestId, randomness, vrfD20.address); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.6/gasUsage.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.6/gasUsage.test.ts new file mode 100644 index 0000000..eccd135 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.6/gasUsage.test.ts @@ -0,0 +1,115 @@ +import { + contract, + matchers, + helpers as h, + setup, +} from '@chainlink/test-helpers' +import { EACAggregatorProxy__factory } from '../../ethers/v0.6/factories/EACAggregatorProxy__factory' +import { AccessControlledAggregator__factory } from '../../ethers/v0.6/factories/AccessControlledAggregator__factory' +import { FluxAggregatorTestHelper__factory } from '../../ethers/v0.6/factories/FluxAggregatorTestHelper__factory' + +let personas: setup.Personas + +const provider = setup.provider() +const linkTokenFactory = new contract.LinkToken__factory() +const aggregatorFactory = new AccessControlledAggregator__factory() +const testHelperFactory = new FluxAggregatorTestHelper__factory() +const proxyFactory = new EACAggregatorProxy__factory() +const emptyAddress = '0x0000000000000000000000000000000000000000' +const decimals = 18 +const phaseBase = h.bigNum(2).pow(64) + +beforeAll(async () => { + const users = await setup.users(provider) + + personas = users.personas +}) + +describe('gas usage', () => { + let aggregator: contract.Instance + let proxy: contract.Instance + let testHelper: contract.Instance + + describe('EACAggreagtorProxy => AccessControlledAggreagtor', () => { + beforeEach(async () => { + await setup.snapshot(provider, async () => { + testHelper = await testHelperFactory.connect(personas.Default).deploy() + const link = await linkTokenFactory.connect(personas.Default).deploy() + aggregator = await (aggregatorFactory as any) + .connect(personas.Default) + .deploy( + link.address, + 0, + 0, + emptyAddress, + 0, + h.bigNum(2).pow(254), + decimals, + h.toBytes32String('TEST/LINK'), + { gasLimit: 8_000_000 }, + ) + proxy = await proxyFactory + .connect(personas.Default) + .deploy(aggregator.address, emptyAddress) + + await aggregator.changeOracles( + [], + [personas.Neil.address], + [personas.Neil.address], + 1, + 1, + 0, + ) + await aggregator.connect(personas.Neil).submit(1, 100) + await aggregator.disableAccessCheck() + await aggregator.addAccess(proxy.address) + })() + }) + + it('#latestAnswer', async () => { + const tx1 = await testHelper.readLatestAnswer(aggregator.address) + const tx2 = await testHelper.readLatestAnswer(proxy.address) + + matchers.gasDiffLessThan(3000, await tx1.wait(), await tx2.wait()) + }) + + it('#latestRound', async () => { + const tx1 = await testHelper.readLatestRound(aggregator.address) + const tx2 = await testHelper.readLatestRound(proxy.address) + + matchers.gasDiffLessThan(3000, await tx1.wait(), await tx2.wait()) + }) + + it('#latestTimestamp', async () => { + const tx1 = await testHelper.readLatestTimestamp(aggregator.address) + const tx2 = await testHelper.readLatestTimestamp(proxy.address) + + matchers.gasDiffLessThan(3000, await tx1.wait(), await tx2.wait()) + }) + + it('#getAnswer', async () => { + const aggId = 1 + const proxyId = phaseBase.add(aggId) + const tx1 = await testHelper.readGetAnswer(aggregator.address, aggId) + const tx2 = await testHelper.readGetAnswer(proxy.address, proxyId) + + matchers.gasDiffLessThan(4000, await tx1.wait(), await tx2.wait()) + }) + + it('#getTimestamp', async () => { + const aggId = 1 + const proxyId = phaseBase.add(aggId) + const tx1 = await testHelper.readGetTimestamp(aggregator.address, aggId) + const tx2 = await testHelper.readGetTimestamp(proxy.address, proxyId) + + matchers.gasDiffLessThan(4000, await tx1.wait(), await tx2.wait()) + }) + + it('#latestRoundData', async () => { + const tx1 = await testHelper.readLatestRoundData(aggregator.address) + const tx2 = await testHelper.readLatestRoundData(proxy.address) + + matchers.gasDiffLessThan(3000, await tx1.wait(), await tx2.wait()) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/AggregatorProxy.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/AggregatorProxy.test.ts new file mode 100644 index 0000000..cf7f7f5 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/AggregatorProxy.test.ts @@ -0,0 +1,613 @@ +import { ethers } from "hardhat"; +import { increaseTimeBy, numToBytes32, publicAbi, toWei } from "../test-helpers/helpers"; +import { assert } from "chai"; +import { BigNumber, constants, Contract, ContractFactory, Signer } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { bigNumEquals, evmRevert } from "../test-helpers/matchers"; + +let personas: Personas; +let defaultAccount: Signer; + +let linkTokenFactory: ContractFactory; +let aggregatorFactory: ContractFactory; +let historicAggregatorFactory: ContractFactory; +let aggregatorFacadeFactory: ContractFactory; +let aggregatorProxyFactory: ContractFactory; +let fluxAggregatorFactory: ContractFactory; +let reverterFactory: ContractFactory; + +before(async () => { + const users = await getUsers(); + + personas = users.personas; + defaultAccount = users.roles.defaultAccount; + + linkTokenFactory = await ethers.getContractFactory("LinkToken", defaultAccount); + aggregatorFactory = await ethers.getContractFactory("MockV3Aggregator", defaultAccount); + historicAggregatorFactory = await ethers.getContractFactory("MockV2Aggregator", defaultAccount); + aggregatorFacadeFactory = await ethers.getContractFactory("AggregatorFacade", defaultAccount); + aggregatorProxyFactory = await ethers.getContractFactory("AggregatorProxy", defaultAccount); + fluxAggregatorFactory = await ethers.getContractFactory("FluxAggregator", defaultAccount); + reverterFactory = await ethers.getContractFactory("Reverter", defaultAccount); +}); + +describe("AggregatorProxy", () => { + const deposit = toWei("100"); + const response = numToBytes32(54321); + const response2 = numToBytes32(67890); + const decimals = 18; + const phaseBase = BigNumber.from(2).pow(64); + + let link: Contract; + let aggregator: Contract; + let aggregator2: Contract; + let historicAggregator: Contract; + let proxy: Contract; + let flux: Contract; + let reverter: Contract; + + beforeEach(async () => { + link = await linkTokenFactory.connect(defaultAccount).deploy(); + aggregator = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response); + await link.transfer(aggregator.address, deposit); + proxy = await aggregatorProxyFactory.connect(defaultAccount).deploy(aggregator.address); + const emptyAddress = constants.AddressZero; + flux = await fluxAggregatorFactory + .connect(personas.Carol) + .deploy(link.address, 0, 0, emptyAddress, 0, 0, 18, "TEST / LINK"); + }); + + it("has a limited public interface", () => { + publicAbi(proxy, [ + "aggregator", + "confirmAggregator", + "decimals", + "description", + "getAnswer", + "getRoundData", + "getTimestamp", + "latestAnswer", + "latestRound", + "latestRoundData", + "latestTimestamp", + "phaseAggregators", + "phaseId", + "proposeAggregator", + "proposedAggregator", + "proposedGetRoundData", + "proposedLatestRoundData", + "version", + // Ownable methods: + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("constructor", () => { + it("sets the proxy phase and aggregator", async () => { + bigNumEquals(1, await proxy.phaseId()); + assert.equal(aggregator.address, await proxy.phaseAggregators(1)); + }); + }); + + describe("#latestRound", () => { + it("pulls the rate from the aggregator", async () => { + bigNumEquals(phaseBase.add(1), await proxy.latestRound()); + }); + }); + + describe("#latestAnswer", () => { + it("pulls the rate from the aggregator", async () => { + bigNumEquals(response, await proxy.latestAnswer()); + const latestRound = await proxy.latestRound(); + bigNumEquals(response, await proxy.getAnswer(latestRound)); + }); + + describe("after being updated to another contract", () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + await link.transfer(aggregator2.address, deposit); + bigNumEquals(response2, await aggregator2.latestAnswer()); + + await proxy.proposeAggregator(aggregator2.address); + await proxy.confirmAggregator(aggregator2.address); + }); + + it("pulls the rate from the new aggregator", async () => { + bigNumEquals(response2, await proxy.latestAnswer()); + const latestRound = await proxy.latestRound(); + bigNumEquals(response2, await proxy.getAnswer(latestRound)); + }); + }); + + describe("when the relevant info is not available", () => { + beforeEach(async () => { + await proxy.proposeAggregator(flux.address); + await proxy.confirmAggregator(flux.address); + }); + + it("does not revert when called with a non existent ID", async () => { + const actual = await proxy.latestAnswer(); + bigNumEquals(0, actual); + }); + }); + }); + + describe("#getAnswer", () => { + describe("when the relevant round is not available", () => { + beforeEach(async () => { + await proxy.proposeAggregator(flux.address); + await proxy.confirmAggregator(flux.address); + }); + + it("does not revert when called with a non existent ID", async () => { + const proxyId = phaseBase.mul(await proxy.phaseId()).add(1); + const actual = await proxy.getAnswer(proxyId); + bigNumEquals(0, actual); + }); + }); + + describe("when the answer reverts in a non-predicted way", () => { + it("reverts", async () => { + reverter = await reverterFactory.connect(defaultAccount).deploy(); + await proxy.proposeAggregator(reverter.address); + await proxy.confirmAggregator(reverter.address); + assert.equal(reverter.address, await proxy.aggregator()); + + const proxyId = phaseBase.mul(await proxy.phaseId()); + + await evmRevert(proxy.getAnswer(proxyId), "Raised by Reverter.sol"); + }); + }); + + describe("after being updated to another contract", () => { + let preUpdateRoundId: BigNumber; + let preUpdateAnswer: BigNumber; + + beforeEach(async () => { + preUpdateRoundId = await proxy.latestRound(); + preUpdateAnswer = await proxy.latestAnswer(); + + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + await link.transfer(aggregator2.address, deposit); + bigNumEquals(response2, await aggregator2.latestAnswer()); + + await proxy.proposeAggregator(aggregator2.address); + await proxy.confirmAggregator(aggregator2.address); + }); + + it("reports answers for previous phases", async () => { + const actualAnswer = await proxy.getAnswer(preUpdateRoundId); + bigNumEquals(preUpdateAnswer, actualAnswer); + }); + }); + + describe("when the relevant info is not available", () => { + it("returns 0", async () => { + const actual = await proxy.getAnswer(phaseBase.mul(777)); + bigNumEquals(0, actual); + }); + }); + + describe("when the round ID is too large", () => { + const overflowRoundId = BigNumber.from(2) + .pow(255) + .add(phaseBase) // get the original phase + .add(1); // get the original round + it("returns 0", async () => { + const actual = await proxy.getTimestamp(overflowRoundId); + bigNumEquals(0, actual); + }); + }); + }); + + describe("#getTimestamp", () => { + describe("when the relevant round is not available", () => { + beforeEach(async () => { + await proxy.proposeAggregator(flux.address); + await proxy.confirmAggregator(flux.address); + }); + + it("does not revert when called with a non existent ID", async () => { + const proxyId = phaseBase.mul(await proxy.phaseId()).add(1); + const actual = await proxy.getTimestamp(proxyId); + bigNumEquals(0, actual); + }); + }); + + describe("when the relevant info is not available", () => { + it("returns 0", async () => { + const actual = await proxy.getTimestamp(phaseBase.mul(777)); + bigNumEquals(0, actual); + }); + }); + + describe("when the round ID is too large", () => { + const overflowRoundId = BigNumber.from(2) + .pow(255) + .add(phaseBase) // get the original phase + .add(1); // get the original round + + it("returns 0", async () => { + const actual = await proxy.getTimestamp(overflowRoundId); + bigNumEquals(0, actual); + }); + }); + }); + + describe("#latestTimestamp", () => { + beforeEach(async () => { + const height = await aggregator.latestTimestamp(); + assert.notEqual("0", height.toString()); + }); + + it("pulls the timestamp from the aggregator", async () => { + bigNumEquals(await aggregator.latestTimestamp(), await proxy.latestTimestamp()); + const latestRound = await proxy.latestRound(); + bigNumEquals(await aggregator.latestTimestamp(), await proxy.getTimestamp(latestRound)); + }); + + describe("after being updated to another contract", () => { + beforeEach(async () => { + await increaseTimeBy(30, ethers.provider); + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + + const height2 = await aggregator2.latestTimestamp(); + assert.notEqual("0", height2.toString()); + + const height1 = await aggregator.latestTimestamp(); + assert.notEqual(height1.toString(), height2.toString(), "Height1 and Height2 should not be equal"); + + await proxy.proposeAggregator(aggregator2.address); + await proxy.confirmAggregator(aggregator2.address); + }); + + it("pulls the timestamp from the new aggregator", async () => { + bigNumEquals(await aggregator2.latestTimestamp(), await proxy.latestTimestamp()); + const latestRound = await proxy.latestRound(); + bigNumEquals(await aggregator2.latestTimestamp(), await proxy.getTimestamp(latestRound)); + }); + }); + }); + + describe("#getRoundData", () => { + describe("when pointed at a Historic Aggregator", () => { + beforeEach(async () => { + historicAggregator = await historicAggregatorFactory.connect(defaultAccount).deploy(response2); + await proxy.proposeAggregator(historicAggregator.address); + await proxy.confirmAggregator(historicAggregator.address); + }); + + it("reverts", async () => { + const latestRoundId = await historicAggregator.latestRound(); + await evmRevert(proxy.getRoundData(latestRoundId)); + }); + + describe("when pointed at an Aggregator Facade", () => { + beforeEach(async () => { + const facade = await aggregatorFacadeFactory + .connect(defaultAccount) + .deploy(aggregator.address, 18, "LINK/USD: Aggregator Facade"); + await proxy.proposeAggregator(facade.address); + await proxy.confirmAggregator(facade.address); + }); + + it("works for a valid roundId", async () => { + const aggId = await aggregator.latestRound(); + const phaseId = phaseBase.mul(await proxy.phaseId()); + const proxyId = phaseId.add(aggId); + + const round = await proxy.getRoundData(proxyId); + bigNumEquals(proxyId, round.id); + bigNumEquals(response, round.answer); + const nowSeconds = new Date().valueOf() / 1000; + assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120); + bigNumEquals(round.updatedAt, round.startedAt); + bigNumEquals(proxyId, round.answeredInRound); + }); + }); + }); + + describe("when pointed at a FluxAggregator", () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + + await proxy.proposeAggregator(aggregator2.address); + await proxy.confirmAggregator(aggregator2.address); + }); + + it("works for a valid round ID", async () => { + const aggId = phaseBase.sub(2); + await aggregator2.connect(personas.Carol).updateRoundData(aggId, response2, 77, 42); + + const phaseId = phaseBase.mul(await proxy.phaseId()); + const proxyId = phaseId.add(aggId); + + const round = await proxy.getRoundData(proxyId); + bigNumEquals(proxyId, round.id); + bigNumEquals(response2, round.answer); + bigNumEquals(42, round.startedAt); + bigNumEquals(77, round.updatedAt); + bigNumEquals(proxyId, round.answeredInRound); + }); + }); + + it("reads round ID of a previous phase", async () => { + const oldphaseId = phaseBase.mul(await proxy.phaseId()); + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + + await proxy.proposeAggregator(aggregator2.address); + await proxy.confirmAggregator(aggregator2.address); + + const aggId = await aggregator.latestRound(); + const proxyId = oldphaseId.add(aggId); + + const round = await proxy.getRoundData(proxyId); + bigNumEquals(proxyId, round.id); + bigNumEquals(response, round.answer); + + const nowSeconds = new Date().valueOf() / 1000; + assert.isAbove(round.startedAt.toNumber(), nowSeconds - 120); + bigNumEquals(round.startedAt, round.updatedAt); + bigNumEquals(proxyId, round.answeredInRound); + }); + }); + + describe("#latestRoundData", () => { + describe("when pointed at a Historic Aggregator", () => { + beforeEach(async () => { + historicAggregator = await historicAggregatorFactory.connect(defaultAccount).deploy(response2); + await proxy.proposeAggregator(historicAggregator.address); + await proxy.confirmAggregator(historicAggregator.address); + }); + + it("reverts", async () => { + await evmRevert(proxy.latestRoundData()); + }); + + describe("when pointed at an Aggregator Facade", () => { + beforeEach(async () => { + const facade = await aggregatorFacadeFactory + .connect(defaultAccount) + .deploy(historicAggregator.address, 17, "DOGE/ZWL: Aggregator Facade"); + await proxy.proposeAggregator(facade.address); + await proxy.confirmAggregator(facade.address); + }); + + it("does not revert", async () => { + const aggId = await historicAggregator.latestRound(); + const phaseId = phaseBase.mul(await proxy.phaseId()); + const proxyId = phaseId.add(aggId); + + const round = await proxy.latestRoundData(); + bigNumEquals(proxyId, round.id); + bigNumEquals(response2, round.answer); + const nowSeconds = new Date().valueOf() / 1000; + assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120); + bigNumEquals(round.updatedAt, round.startedAt); + bigNumEquals(proxyId, round.answeredInRound); + }); + + it("uses the decimals set in the constructor", async () => { + bigNumEquals(17, await proxy.decimals()); + }); + + it("uses the description set in the constructor", async () => { + assert.equal("DOGE/ZWL: Aggregator Facade", await proxy.description()); + }); + + it("sets the version to 2", async () => { + bigNumEquals(2, await proxy.version()); + }); + }); + }); + + describe("when pointed at a FluxAggregator", () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + + await proxy.proposeAggregator(aggregator2.address); + await proxy.confirmAggregator(aggregator2.address); + }); + + it("does not revert", async () => { + const aggId = phaseBase.sub(2); + await aggregator2.connect(personas.Carol).updateRoundData(aggId, response2, 77, 42); + + const phaseId = phaseBase.mul(await proxy.phaseId()); + const proxyId = phaseId.add(aggId); + + const round = await proxy.latestRoundData(); + bigNumEquals(proxyId, round.id); + bigNumEquals(response2, round.answer); + bigNumEquals(42, round.startedAt); + bigNumEquals(77, round.updatedAt); + bigNumEquals(proxyId, round.answeredInRound); + }); + + it("uses the decimals of the aggregator", async () => { + bigNumEquals(18, await proxy.decimals()); + }); + + it("uses the description of the aggregator", async () => { + assert.equal("v0.6/tests/MockV3Aggregator.sol", await proxy.description()); + }); + + it("uses the version of the aggregator", async () => { + bigNumEquals(0, await proxy.version()); + }); + }); + }); + + describe("#proposeAggregator", () => { + beforeEach(async () => { + await proxy.transferOwnership(await personas.Carol.getAddress()); + await proxy.connect(personas.Carol).acceptOwnership(); + + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, 1); + + assert.equal(aggregator.address, await proxy.aggregator()); + }); + + describe("when called by the owner", () => { + it("sets the address of the proposed aggregator", async () => { + await proxy.connect(personas.Carol).proposeAggregator(aggregator2.address); + + assert.equal(aggregator2.address, await proxy.proposedAggregator()); + }); + + it("emits an AggregatorProposed event", async () => { + const tx = await proxy.connect(personas.Carol).proposeAggregator(aggregator2.address); + const receipt = await tx.wait(); + const eventLog = receipt?.events; + + assert.equal(eventLog?.length, 1); + assert.equal(eventLog?.[0].event, "AggregatorProposed"); + assert.equal(eventLog?.[0].args?.[0], aggregator.address); + assert.equal(eventLog?.[0].args?.[1], aggregator2.address); + }); + }); + + describe("when called by a non-owner", () => { + it("does not update", async () => { + await evmRevert(proxy.connect(personas.Neil).proposeAggregator(aggregator2.address), "Only callable by owner"); + + assert.equal(aggregator.address, await proxy.aggregator()); + }); + }); + }); + + describe("#confirmAggregator", () => { + beforeEach(async () => { + await proxy.transferOwnership(await personas.Carol.getAddress()); + await proxy.connect(personas.Carol).acceptOwnership(); + + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, 1); + + assert.equal(aggregator.address, await proxy.aggregator()); + }); + + describe("when called by the owner", () => { + beforeEach(async () => { + await proxy.connect(personas.Carol).proposeAggregator(aggregator2.address); + }); + + it("sets the address of the new aggregator", async () => { + await proxy.connect(personas.Carol).confirmAggregator(aggregator2.address); + + assert.equal(aggregator2.address, await proxy.aggregator()); + }); + + it("increases the phase", async () => { + bigNumEquals(1, await proxy.phaseId()); + + await proxy.connect(personas.Carol).confirmAggregator(aggregator2.address); + + bigNumEquals(2, await proxy.phaseId()); + }); + + it("increases the round ID", async () => { + bigNumEquals(phaseBase.add(1), await proxy.latestRound()); + + await proxy.connect(personas.Carol).confirmAggregator(aggregator2.address); + + bigNumEquals(phaseBase.mul(2).add(1), await proxy.latestRound()); + }); + + it("sets the proxy phase and aggregator", async () => { + assert.equal("0x0000000000000000000000000000000000000000", await proxy.phaseAggregators(2)); + + await proxy.connect(personas.Carol).confirmAggregator(aggregator2.address); + + assert.equal(aggregator2.address, await proxy.phaseAggregators(2)); + }); + + it("emits an AggregatorConfirmed event", async () => { + const tx = await proxy.connect(personas.Carol).confirmAggregator(aggregator2.address); + const receipt = await tx.wait(); + const eventLog = receipt?.events; + + assert.equal(eventLog?.length, 1); + assert.equal(eventLog?.[0].event, "AggregatorConfirmed"); + assert.equal(eventLog?.[0].args?.[0], aggregator.address); + assert.equal(eventLog?.[0].args?.[1], aggregator2.address); + }); + }); + + describe("when called by a non-owner", () => { + beforeEach(async () => { + await proxy.connect(personas.Carol).proposeAggregator(aggregator2.address); + }); + + it("does not update", async () => { + await evmRevert(proxy.connect(personas.Neil).confirmAggregator(aggregator2.address), "Only callable by owner"); + + assert.equal(aggregator.address, await proxy.aggregator()); + }); + }); + }); + + describe("#proposedGetRoundData", () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + }); + + describe("when an aggregator has been proposed", () => { + beforeEach(async () => { + await proxy.connect(defaultAccount).proposeAggregator(aggregator2.address); + assert.equal(await proxy.proposedAggregator(), aggregator2.address); + }); + + it("returns the data for the proposed aggregator", async () => { + const roundId = await aggregator2.latestRound(); + const round = await proxy.proposedGetRoundData(roundId); + bigNumEquals(roundId, round.id); + bigNumEquals(response2, round.answer); + }); + + describe("after the aggregator has been confirmed", () => { + beforeEach(async () => { + await proxy.connect(defaultAccount).confirmAggregator(aggregator2.address); + assert.equal(await proxy.aggregator(), aggregator2.address); + }); + + it("reverts", async () => { + const roundId = await aggregator2.latestRound(); + await evmRevert(proxy.proposedGetRoundData(roundId), "No proposed aggregator present"); + }); + }); + }); + }); + + describe("#proposedLatestRoundData", () => { + beforeEach(async () => { + aggregator2 = await aggregatorFactory.connect(defaultAccount).deploy(decimals, response2); + }); + + describe("when an aggregator has been proposed", () => { + beforeEach(async () => { + await proxy.connect(defaultAccount).proposeAggregator(aggregator2.address); + assert.equal(await proxy.proposedAggregator(), aggregator2.address); + }); + + it("returns the data for the proposed aggregator", async () => { + const roundId = await aggregator2.latestRound(); + const round = await proxy.proposedLatestRoundData(); + bigNumEquals(roundId, round.id); + bigNumEquals(response2, round.answer); + }); + + describe("after the aggregator has been confirmed", () => { + beforeEach(async () => { + await proxy.connect(defaultAccount).confirmAggregator(aggregator2.address); + assert.equal(await proxy.aggregator(), aggregator2.address); + }); + + it("reverts", async () => { + await evmRevert(proxy.proposedLatestRoundData(), "No proposed aggregator present"); + }); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/AuthorizedForwarder.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/AuthorizedForwarder.test.ts new file mode 100644 index 0000000..b97a0d7 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/AuthorizedForwarder.test.ts @@ -0,0 +1,279 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Contract, ContractFactory, ContractReceipt } from "ethers"; +import { getUsers, Roles } from "../test-helpers/setup"; +import { evmRevert } from "../test-helpers/matchers"; + +let getterSetterFactory: ContractFactory; +let forwarderFactory: ContractFactory; +let linkTokenFactory: ContractFactory; + +let roles: Roles; +const zeroAddress = ethers.constants.AddressZero; + +before(async () => { + const users = await getUsers(); + + roles = users.roles; + getterSetterFactory = await ethers.getContractFactory( + "src/v0.4/tests/GetterSetter.sol:GetterSetter", + roles.defaultAccount, + ); + forwarderFactory = await ethers.getContractFactory("AuthorizedForwarder", roles.defaultAccount); + linkTokenFactory = await ethers.getContractFactory("LinkToken", roles.defaultAccount); +}); + +describe("AuthorizedForwarder", () => { + let link: Contract; + let forwarder: Contract; + + beforeEach(async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy(); + forwarder = await forwarderFactory + .connect(roles.defaultAccount) + .deploy(link.address, await roles.defaultAccount.getAddress(), zeroAddress, "0x"); + }); + + it("has a limited public interface", () => { + publicAbi(forwarder, [ + "forward", + "getAuthorizedSenders", + "getChainlinkToken", + "isAuthorizedSender", + "ownerForward", + "setAuthorizedSenders", + "transferOwnershipWithMessage", + // ConfirmedOwner + "transferOwnership", + "acceptOwnership", + "owner", + ]); + }); + + describe("deployment", () => { + it("sets the correct link token", async () => { + assert.equal(await forwarder.getChainlinkToken(), link.address); + }); + + it("sets no authorized senders", async () => { + const senders = await forwarder.getAuthorizedSenders(); + assert.equal(senders.length, 0); + }); + }); + + describe("#setAuthorizedSenders", () => { + let newSenders: string[]; + let receipt: ContractReceipt; + describe("when called by the owner", () => { + describe("setting 3 authorized senders", () => { + beforeEach(async () => { + newSenders = [ + await roles.oracleNode1.getAddress(), + await roles.oracleNode2.getAddress(), + await roles.oracleNode3.getAddress(), + ]; + const tx = await forwarder.connect(roles.defaultAccount).setAuthorizedSenders(newSenders); + receipt = await tx.wait(); + }); + + it("adds the authorized nodes", async () => { + const authorizedSenders = await forwarder.getAuthorizedSenders(); + assert.equal(newSenders.length, authorizedSenders.length); + for (let i = 0; i < authorizedSenders.length; i++) { + assert.equal(authorizedSenders[i], newSenders[i]); + } + }); + + it("emits an event", async () => { + assert.equal(receipt.events?.length, 1); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "AuthorizedSendersChanged"); + const encodedSenders = ethers.utils.defaultAbiCoder.encode( + ["address[]", "address"], + [newSenders, await roles.defaultAccount.getAddress()], + ); + assert.equal(responseEvent?.data, encodedSenders); + }); + + it("replaces the authorized nodes", async () => { + const newSenders = await forwarder.connect(roles.defaultAccount).getAuthorizedSenders(); + assert.notIncludeOrderedMembers(newSenders, [await roles.oracleNode.getAddress()]); + }); + + after(async () => { + await forwarder.connect(roles.defaultAccount).setAuthorizedSenders([await roles.oracleNode.getAddress()]); + }); + }); + + describe("setting 0 authorized senders", () => { + beforeEach(async () => { + newSenders = []; + }); + + it("reverts with a minimum senders message", async () => { + await evmRevert( + forwarder.connect(roles.defaultAccount).setAuthorizedSenders(newSenders), + "Must have at least 1 authorized sender", + ); + }); + }); + }); + + describe("when called by a non-owner", () => { + it("cannot add an authorized node", async () => { + await evmRevert( + forwarder.connect(roles.stranger).setAuthorizedSenders([await roles.stranger.getAddress()]), + "Cannot set authorized senders", + ); + }); + }); + }); + + describe("#forward", () => { + let bytes: string; + let payload: string; + let mock: Contract; + + beforeEach(async () => { + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy(); + bytes = ethers.utils.hexlify(ethers.utils.randomBytes(100)); + payload = getterSetterFactory.interface.encodeFunctionData( + getterSetterFactory.interface.getFunction("setBytes"), + [bytes], + ); + }); + + describe("when called by an unauthorized node", () => { + it("reverts", async () => { + await evmRevert(forwarder.connect(roles.stranger).forward(mock.address, payload)); + }); + }); + + describe("when called by an authorized node", () => { + beforeEach(async () => { + await forwarder.connect(roles.defaultAccount).setAuthorizedSenders([await roles.defaultAccount.getAddress()]); + }); + + describe("when sending to a non-contract address", () => { + it("reverts", async () => { + await evmRevert( + forwarder.connect(roles.defaultAccount).forward(zeroAddress, payload), + "Must forward to a contract", + ); + }); + }); + + describe("when attempting to forward to the link token", () => { + it("reverts", async () => { + const sighash = linkTokenFactory.interface.getSighash("name"); // any Link Token function + await evmRevert(forwarder.connect(roles.defaultAccount).forward(link.address, sighash)); + }); + }); + + describe("when forwarding to any other address", () => { + it("forwards the data", async () => { + const tx = await forwarder.connect(roles.defaultAccount).forward(mock.address, payload); + await tx.wait(); + assert.equal(await mock.getBytes(), bytes); + }); + + it("perceives the message is sent by the AuthorizedForwarder", async () => { + const tx = await forwarder.connect(roles.defaultAccount).forward(mock.address, payload); + await expect(tx).to.emit(mock, "SetBytes").withArgs(forwarder.address, bytes); + }); + }); + }); + }); + + describe("#transferOwnershipWithMessage", () => { + const message = "0x42"; + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert( + forwarder.connect(roles.stranger).transferOwnershipWithMessage(await roles.stranger.getAddress(), message), + "Only callable by owner", + ); + }); + }); + + describe("when called by the owner", () => { + it("calls the normal ownership transfer proposal", async () => { + const tx = await forwarder + .connect(roles.defaultAccount) + .transferOwnershipWithMessage(await roles.stranger.getAddress(), message); + const receipt = await tx.wait(); + + assert.equal(receipt?.events?.[0]?.event, "OwnershipTransferRequested"); + assert.equal(receipt?.events?.[0]?.address, forwarder.address); + assert.equal(receipt?.events?.[0]?.args?.[0], await roles.defaultAccount.getAddress()); + assert.equal(receipt?.events?.[0]?.args?.[1], await roles.stranger.getAddress()); + }); + + it("calls the normal ownership transfer proposal", async () => { + const tx = await forwarder + .connect(roles.defaultAccount) + .transferOwnershipWithMessage(await roles.stranger.getAddress(), message); + const receipt = await tx.wait(); + + assert.equal(receipt?.events?.[1]?.event, "OwnershipTransferRequestedWithMessage"); + assert.equal(receipt?.events?.[1]?.address, forwarder.address); + assert.equal(receipt?.events?.[1]?.args?.[0], await roles.defaultAccount.getAddress()); + assert.equal(receipt?.events?.[1]?.args?.[1], await roles.stranger.getAddress()); + assert.equal(receipt?.events?.[1]?.args?.[2], message); + }); + }); + }); + + describe("#ownerForward", () => { + let bytes: string; + let payload: string; + let mock: Contract; + + beforeEach(async () => { + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy(); + bytes = ethers.utils.hexlify(ethers.utils.randomBytes(100)); + payload = getterSetterFactory.interface.encodeFunctionData( + getterSetterFactory.interface.getFunction("setBytes"), + [bytes], + ); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert(forwarder.connect(roles.stranger).ownerForward(mock.address, payload)); + }); + }); + + describe("when called by owner", () => { + describe("when attempting to forward to the link token", () => { + it("does not revert", async () => { + const sighash = linkTokenFactory.interface.getSighash("name"); // any Link Token function + + await forwarder.connect(roles.defaultAccount).ownerForward(link.address, sighash); + }); + }); + + describe("when forwarding to any other address", () => { + it("forwards the data", async () => { + const tx = await forwarder.connect(roles.defaultAccount).ownerForward(mock.address, payload); + await tx.wait(); + assert.equal(await mock.getBytes(), bytes); + }); + + it("reverts when sending to a non-contract address", async () => { + await evmRevert( + forwarder.connect(roles.defaultAccount).ownerForward(zeroAddress, payload), + "Must forward to a contract", + ); + }); + + it("perceives the message is sent by the Operator", async () => { + const tx = await forwarder.connect(roles.defaultAccount).ownerForward(mock.address, payload); + await expect(tx).to.emit(mock, "SetBytes").withArgs(forwarder.address, bytes); + }); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts new file mode 100644 index 0000000..f505f42 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts @@ -0,0 +1,336 @@ +import { ethers } from "hardhat"; +import { evmWordToAddress, getLogs, publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { BigNumber, Contract, ContractFactory, ContractTransaction } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { evmRevert } from "../test-helpers/matchers"; + +let personas: Personas; +let validatorFactory: ContractFactory; +let acFactory: ContractFactory; +let flagsFactory: ContractFactory; +let aggregatorFactory: ContractFactory; +let compoundOracleFactory: ContractFactory; + +before(async () => { + personas = (await getUsers()).personas; + + validatorFactory = await ethers.getContractFactory("CompoundPriceFlaggingValidator", personas.Carol); + acFactory = await ethers.getContractFactory("SimpleWriteAccessController", personas.Carol); + flagsFactory = await ethers.getContractFactory("Flags", personas.Carol); + aggregatorFactory = await ethers.getContractFactory("MockV3Aggregator", personas.Carol); + compoundOracleFactory = await ethers.getContractFactory("MockCompoundOracle", personas.Carol); +}); + +describe("CompoundPriceFlaggingVlidator", () => { + let validator: Contract; + let aggregator: Contract; + let compoundOracle: Contract; + let flags: Contract; + let ac: Contract; + + const aggregatorDecimals = 18; + // 1000 + const initialAggregatorPrice = BigNumber.from("1000000000000000000000"); + + const compoundSymbol = "ETH"; + const compoundDecimals = 6; + // 1100 (10% deviation from aggregator price) + const initialCompoundPrice = BigNumber.from("1100000000"); + + // (50,000,000 / 1,000,000,000) = 0.05 = 5% deviation threshold + const initialDeviationNumerator = 50_000_000; + + beforeEach(async () => { + ac = await acFactory.connect(personas.Carol).deploy(); + flags = await flagsFactory.connect(personas.Carol).deploy(ac.address); + aggregator = await aggregatorFactory.connect(personas.Carol).deploy(aggregatorDecimals, initialAggregatorPrice); + compoundOracle = await compoundOracleFactory.connect(personas.Carol).deploy(); + await compoundOracle.setPrice(compoundSymbol, initialCompoundPrice, compoundDecimals); + validator = await validatorFactory.connect(personas.Carol).deploy(flags.address, compoundOracle.address); + await validator + .connect(personas.Carol) + .setFeedDetails(aggregator.address, compoundSymbol, compoundDecimals, initialDeviationNumerator); + await ac.connect(personas.Carol).addAccess(validator.address); + }); + + it("has a limited public interface", () => { + publicAbi(validator, [ + "update", + "check", + "setFeedDetails", + "setFlagsAddress", + "setCompoundOpenOracleAddress", + "getFeedDetails", + "flags", + "compoundOpenOracle", + // Upkeep methods: + "checkUpkeep", + "performUpkeep", + // Owned methods: + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#constructor", () => { + it("sets the owner", async () => { + assert.equal(await validator.owner(), await personas.Carol.getAddress()); + }); + + it("sets the arguments passed in", async () => { + assert.equal(await validator.flags(), flags.address); + assert.equal(await validator.compoundOpenOracle(), compoundOracle.address); + }); + }); + + describe("#setOpenOracleAddress", () => { + let newCompoundOracle: Contract; + let tx: ContractTransaction; + + beforeEach(async () => { + newCompoundOracle = await compoundOracleFactory.connect(personas.Carol).deploy(); + tx = await validator.connect(personas.Carol).setCompoundOpenOracleAddress(newCompoundOracle.address); + }); + + it("changes the compound oracke address", async () => { + assert.equal(await validator.compoundOpenOracle(), newCompoundOracle.address); + }); + + it("emits a log event", async () => { + await expect(tx) + .to.emit(validator, "CompoundOpenOracleAddressUpdated") + .withArgs(compoundOracle.address, newCompoundOracle.address); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert( + validator.connect(personas.Neil).setCompoundOpenOracleAddress(newCompoundOracle.address), + "Only callable by owner", + ); + }); + }); + }); + + describe("#setFlagsAddress", () => { + let newFlagsContract: Contract; + let tx: ContractTransaction; + + beforeEach(async () => { + newFlagsContract = await flagsFactory.connect(personas.Carol).deploy(ac.address); + tx = await validator.connect(personas.Carol).setFlagsAddress(newFlagsContract.address); + }); + + it("changes the flags address", async () => { + assert.equal(await validator.flags(), newFlagsContract.address); + }); + + it("emits a log event", async () => { + await expect(tx).to.emit(validator, "FlagsAddressUpdated").withArgs(flags.address, newFlagsContract.address); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert( + validator.connect(personas.Neil).setFlagsAddress(newFlagsContract.address), + "Only callable by owner", + ); + }); + }); + }); + + describe("#setFeedDetails", () => { + let mockAggregator: Contract; + let tx: ContractTransaction; + const symbol = "BTC"; + const decimals = 8; + const deviationNumerator = 50_000_000; // 5% + + beforeEach(async () => { + await compoundOracle.connect(personas.Carol).setPrice("BTC", 1500000, 2); + mockAggregator = await aggregatorFactory.connect(personas.Carol).deploy(decimals, 4000000000000); + tx = await validator + .connect(personas.Carol) + .setFeedDetails(mockAggregator.address, symbol, decimals, deviationNumerator); + }); + + it("sets the correct state", async () => { + const response = await validator.connect(personas.Carol).getFeedDetails(mockAggregator.address); + + assert.equal(response[0], symbol); + assert.equal(response[1], decimals); + assert.equal(response[2].toString(), deviationNumerator.toString()); + }); + + it("uses the existing symbol if one already exists", async () => { + const newSymbol = "LINK"; + + await compoundOracle.connect(personas.Carol).setPrice(newSymbol, 1500000, 2); + + tx = await validator + .connect(personas.Carol) + .setFeedDetails(mockAggregator.address, newSymbol, decimals, deviationNumerator); + + // Check the event + await expect(tx) + .to.emit(validator, "FeedDetailsSet") + .withArgs(mockAggregator.address, symbol, decimals, deviationNumerator); + + // Check the state + const response = await validator.connect(personas.Carol).getFeedDetails(mockAggregator.address); + assert.equal(response[0], symbol); + }); + + it("emits an event", async () => { + await expect(tx) + .to.emit(validator, "FeedDetailsSet") + .withArgs(mockAggregator.address, symbol, decimals, deviationNumerator); + }); + + it("fails when given a 0 numerator", async () => { + await evmRevert( + validator.connect(personas.Carol).setFeedDetails(mockAggregator.address, symbol, decimals, 0), + "Invalid threshold numerator", + ); + }); + + it("fails when given a numerator above 1 billion", async () => { + await evmRevert( + validator.connect(personas.Carol).setFeedDetails(mockAggregator.address, symbol, decimals, 1_200_000_000), + "Invalid threshold numerator", + ); + }); + + it("fails when the compound price is invalid", async () => { + await evmRevert( + validator.connect(personas.Carol).setFeedDetails(mockAggregator.address, "TEST", decimals, deviationNumerator), + "Invalid Compound price", + ); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert( + validator.connect(personas.Neil).setFeedDetails(mockAggregator.address, symbol, decimals, deviationNumerator), + "Only callable by owner", + ); + }); + }); + }); + + describe("#check", () => { + describe("with a single aggregator", () => { + describe("with a deviated price exceding threshold", () => { + it("returns the deviated aggregator", async () => { + const aggregators = [aggregator.address]; + const response = await validator.check(aggregators); + assert.equal(response.length, 1); + assert.equal(response[0], aggregator.address); + }); + }); + + describe("with a price within the threshold", () => { + const newCompoundPrice = BigNumber.from("1000000000"); + beforeEach(async () => { + await compoundOracle.setPrice("ETH", newCompoundPrice, compoundDecimals); + }); + + it("returns an empty array", async () => { + const aggregators = [aggregator.address]; + const response = await validator.check(aggregators); + assert.equal(response.length, 0); + }); + }); + }); + }); + + describe("#update", () => { + describe("with a single aggregator", () => { + describe("with a deviated price exceding threshold", () => { + it("raises a flag on the flags contract", async () => { + const aggregators = [aggregator.address]; + const tx = await validator.connect(personas.Carol).update(aggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 1); + assert.equal(evmWordToAddress(logs[0].topics[1]), aggregator.address); + }); + }); + + describe("with a price within the threshold", () => { + const newCompoundPrice = BigNumber.from("1000000000"); + beforeEach(async () => { + await compoundOracle.setPrice("ETH", newCompoundPrice, compoundDecimals); + }); + + it("does nothing", async () => { + const aggregators = [aggregator.address]; + const tx = await validator.connect(personas.Carol).update(aggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + }); + }); + + describe("#checkUpkeep", () => { + describe("with a single aggregator", () => { + describe("with a deviated price exceding threshold", () => { + it("returns the deviated aggregator", async () => { + const aggregators = [aggregator.address]; + const encodedAggregators = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const response = await validator.connect(personas.Carol).checkUpkeep(encodedAggregators); + + const decodedResponse = ethers.utils.defaultAbiCoder.decode(["address[]"], response?.[1]); + assert.equal(decodedResponse?.[0]?.[0], aggregators[0]); + }); + }); + + describe("with a price within the threshold", () => { + const newCompoundPrice = BigNumber.from("1000000000"); + beforeEach(async () => { + await compoundOracle.setPrice("ETH", newCompoundPrice, compoundDecimals); + }); + + it("returns an empty array", async () => { + const aggregators = [aggregator.address]; + const encodedAggregators = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const response = await validator.connect(personas.Carol).checkUpkeep(encodedAggregators); + const decodedResponse = ethers.utils.defaultAbiCoder.decode(["address[]"], response?.[1]); + assert.equal(decodedResponse?.[0]?.length, 0); + }); + }); + }); + }); + + describe("#performUpkeep", () => { + describe("with a single aggregator", () => { + describe("with a deviated price exceding threshold", () => { + it("raises a flag on the flags contract", async () => { + const aggregators = [aggregator.address]; + const encodedAggregators = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const tx = await validator.connect(personas.Carol).performUpkeep(encodedAggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 1); + assert.equal(evmWordToAddress(logs[0].topics[1]), aggregator.address); + }); + }); + + describe("with a price within the threshold", () => { + const newCompoundPrice = BigNumber.from("1000000000"); + beforeEach(async () => { + await compoundOracle.setPrice("ETH", newCompoundPrice, compoundDecimals); + }); + + it("does nothing", async () => { + const aggregators = [aggregator.address]; + const encodedAggregators = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const tx = await validator.connect(personas.Carol).performUpkeep(encodedAggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/ConfirmedOwner.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/ConfirmedOwner.test.ts new file mode 100644 index 0000000..214a5e2 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/ConfirmedOwner.test.ts @@ -0,0 +1,112 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Contract, ContractFactory, Signer } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { evmRevert } from "../test-helpers/matchers"; + +let confirmedOwnerTestHelperFactory: ContractFactory; +let confirmedOwnerFactory: ContractFactory; + +let personas: Personas; +let owner: Signer; +let nonOwner: Signer; +let newOwner: Signer; + +before(async () => { + const users = await getUsers(); + personas = users.personas; + owner = personas.Carol; + nonOwner = personas.Neil; + newOwner = personas.Ned; + + confirmedOwnerTestHelperFactory = await ethers.getContractFactory("ConfirmedOwnerTestHelper", owner); + confirmedOwnerFactory = await ethers.getContractFactory("src/v0.7/dev/ConfirmedOwner.sol:ConfirmedOwner", owner); +}); + +describe("ConfirmedOwner", () => { + let confirmedOwner: Contract; + + beforeEach(async () => { + confirmedOwner = await confirmedOwnerTestHelperFactory.connect(owner).deploy(); + }); + + it("has a limited public interface", () => { + publicAbi(confirmedOwner, [ + "acceptOwnership", + "owner", + "transferOwnership", + // test helper public methods + "modifierOnlyOwner", + ]); + }); + + describe("#constructor", () => { + it("assigns ownership to the deployer", async () => { + const [actual, expected] = await Promise.all([owner.getAddress(), confirmedOwner.owner()]); + + assert.equal(actual, expected); + }); + + it("reverts if assigned to the zero address", async () => { + await evmRevert( + confirmedOwnerFactory.connect(owner).deploy(ethers.constants.AddressZero), + "Cannot set owner to zero", + ); + }); + }); + + describe("#onlyOwner modifier", () => { + describe("when called by an owner", () => { + it("successfully calls the method", async () => { + const tx = await confirmedOwner.connect(owner).modifierOnlyOwner(); + await expect(tx).to.emit(confirmedOwner, "Here"); + }); + }); + + describe("when called by anyone but the owner", () => { + it("reverts", async () => await evmRevert(confirmedOwner.connect(nonOwner).modifierOnlyOwner())); + }); + }); + + describe("#transferOwnership", () => { + describe("when called by an owner", () => { + it("emits a log", async () => { + const tx = await confirmedOwner.connect(owner).transferOwnership(await newOwner.getAddress()); + await expect(tx) + .to.emit(confirmedOwner, "OwnershipTransferRequested") + .withArgs(await owner.getAddress(), await newOwner.getAddress()); + }); + + it("does not allow ownership transfer to self", async () => { + await evmRevert( + confirmedOwner.connect(owner).transferOwnership(await owner.getAddress()), + "Cannot transfer to self", + ); + }); + }); + }); + + describe("when called by anyone but the owner", () => { + it("successfully calls the method", async () => + await evmRevert(confirmedOwner.connect(nonOwner).transferOwnership(await newOwner.getAddress()))); + }); + + describe("#acceptOwnership", () => { + describe("after #transferOwnership has been called", () => { + beforeEach(async () => { + await confirmedOwner.connect(owner).transferOwnership(await newOwner.getAddress()); + }); + + it("allows the recipient to call it", async () => { + const tx = await confirmedOwner.connect(newOwner).acceptOwnership(); + await expect(tx) + .to.emit(confirmedOwner, "OwnershipTransferred") + .withArgs(await owner.getAddress(), await newOwner.getAddress()); + }); + + it("does not allow a non-recipient to call it", async () => + await evmRevert(confirmedOwner.connect(nonOwner).acceptOwnership())); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/Operator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/Operator.test.ts new file mode 100644 index 0000000..3b39545 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/Operator.test.ts @@ -0,0 +1,2523 @@ +import { ethers } from "hardhat"; +import { publicAbi, toBytes32String, toWei, stringToBytes, increaseTime5Minutes } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { BigNumber, constants, Contract, ContractFactory, ContractReceipt, ContractTransaction, Signer } from "ethers"; +import { getUsers, Roles } from "../test-helpers/setup"; +import { bigNumEquals, evmRevert } from "../test-helpers/matchers"; +import type { providers } from "ethers"; +import { + convertCancelParams, + convertFufillParams, + convertFulfill2Params, + decodeRunRequest, + encodeOracleRequest, + encodeRequestOracleData, + RunRequest, +} from "../test-helpers/oracle"; + +let v7ConsumerFactory: ContractFactory; +let basicConsumerFactory: ContractFactory; +let multiWordConsumerFactory: ContractFactory; +let gasGuzzlingConsumerFactory: ContractFactory; +let getterSetterFactory: ContractFactory; +let maliciousRequesterFactory: ContractFactory; +let maliciousConsumerFactory: ContractFactory; +let maliciousMultiWordConsumerFactory: ContractFactory; +let operatorFactory: ContractFactory; +let forwarderFactory: ContractFactory; +let linkTokenFactory: ContractFactory; +const zeroAddress = ethers.constants.AddressZero; + +let roles: Roles; + +before(async () => { + const users = await getUsers(); + + roles = users.roles; + v7ConsumerFactory = await ethers.getContractFactory("src/v0.7/tests/Consumer.sol:Consumer"); + basicConsumerFactory = await ethers.getContractFactory("src/v0.6/tests/BasicConsumer.sol:BasicConsumer"); + multiWordConsumerFactory = await ethers.getContractFactory("src/v0.6/tests/MultiWordConsumer.sol:MultiWordConsumer"); + gasGuzzlingConsumerFactory = await ethers.getContractFactory( + "src/v0.6/tests/GasGuzzlingConsumer.sol:GasGuzzlingConsumer", + ); + getterSetterFactory = await ethers.getContractFactory("src/v0.4/tests/GetterSetter.sol:GetterSetter"); + maliciousRequesterFactory = await ethers.getContractFactory( + "src/v0.4/tests/MaliciousRequester.sol:MaliciousRequester", + ); + maliciousConsumerFactory = await ethers.getContractFactory("src/v0.4/tests/MaliciousConsumer.sol:MaliciousConsumer"); + maliciousMultiWordConsumerFactory = await ethers.getContractFactory("MaliciousMultiWordConsumer"); + operatorFactory = await ethers.getContractFactory("Operator"); + forwarderFactory = await ethers.getContractFactory("AuthorizedForwarder"); + linkTokenFactory = await ethers.getContractFactory("LinkToken"); +}); + +describe("Operator", () => { + let fHash: string; + let specId: string; + let to: string; + let link: Contract; + let operator: Contract; + let forwarder1: Contract; + let forwarder2: Contract; + let owner: Signer; + + beforeEach(async () => { + fHash = getterSetterFactory.interface.getSighash("requestedBytes32"); + specId = "0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000"; + to = "0x80e29acb842498fe6591f020bd82766dce619d43"; + link = await linkTokenFactory.connect(roles.defaultAccount).deploy(); + owner = roles.defaultAccount; + operator = await operatorFactory.connect(owner).deploy(link.address, await owner.getAddress()); + await operator.connect(roles.defaultAccount).setAuthorizedSenders([await roles.oracleNode.getAddress()]); + }); + + it("has a limited public interface", () => { + publicAbi(operator, [ + "acceptAuthorizedReceivers", + "acceptOwnableContracts", + "cancelOracleRequest", + "distributeFunds", + "fulfillOracleRequest", + "fulfillOracleRequest2", + "getAuthorizedSenders", + "getChainlinkToken", + "getExpiryTime", + "isAuthorizedSender", + "onTokenTransfer", + "oracleRequest", + "ownerForward", + "ownerTransferAndCall", + "requestOracleData", + "setAuthorizedSenders", + "setAuthorizedSendersOn", + "transferOwnableContracts", + "withdraw", + "withdrawable", + // Ownable methods: + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#transferOwnableContracts", () => { + beforeEach(async () => { + forwarder1 = await forwarderFactory.connect(owner).deploy(link.address, operator.address, zeroAddress, "0x"); + forwarder2 = await forwarderFactory.connect(owner).deploy(link.address, operator.address, zeroAddress, "0x"); + }); + + describe("being called by the owner", () => { + it("cannot transfer to self", async () => { + await evmRevert( + operator.connect(owner).transferOwnableContracts([forwarder1.address], operator.address), + "Cannot transfer to self", + ); + }); + + it("emits an ownership transfer request event", async () => { + const tx = await operator + .connect(owner) + .transferOwnableContracts([forwarder1.address, forwarder2.address], await roles.oracleNode1.getAddress()); + const receipt = await tx.wait(); + assert.equal(receipt?.events?.length, 2); + const log1 = receipt?.events?.[0]; + assert.equal(log1?.event, "OwnershipTransferRequested"); + assert.equal(log1?.address, forwarder1.address); + assert.equal(log1?.args?.[0], operator.address); + assert.equal(log1?.args?.[1], await roles.oracleNode1.getAddress()); + const log2 = receipt?.events?.[1]; + assert.equal(log2?.event, "OwnershipTransferRequested"); + assert.equal(log2?.address, forwarder2.address); + assert.equal(log2?.args?.[0], operator.address); + assert.equal(log2?.args?.[1], await roles.oracleNode1.getAddress()); + }); + }); + + describe("being called by a non-owner", () => { + it("reverts with message", async () => { + await evmRevert( + operator + .connect(roles.stranger) + .transferOwnableContracts([forwarder1.address], await roles.oracleNode2.getAddress()), + "Only callable by owner", + ); + }); + }); + }); + + describe("#acceptOwnableContracts", () => { + describe("being called by the owner", () => { + let operator2: Contract; + let receipt: ContractReceipt; + + beforeEach(async () => { + operator2 = await operatorFactory + .connect(roles.defaultAccount) + .deploy(link.address, await roles.defaultAccount.getAddress()); + forwarder1 = await forwarderFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, zeroAddress, "0x"); + forwarder2 = await forwarderFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, zeroAddress, "0x"); + await operator + .connect(roles.defaultAccount) + .transferOwnableContracts([forwarder1.address, forwarder2.address], operator2.address); + const tx = await operator2 + .connect(roles.defaultAccount) + .acceptOwnableContracts([forwarder1.address, forwarder2.address]); + receipt = await tx.wait(); + }); + + it("sets the new owner on the forwarder", async () => { + assert.equal(await forwarder1.owner(), operator2.address); + }); + + it("emits ownership transferred events", async () => { + assert.equal(receipt?.events?.[0]?.event, "OwnershipTransferred"); + assert.equal(receipt?.events?.[0]?.address, forwarder1.address); + assert.equal(receipt?.events?.[0]?.args?.[0], operator.address); + assert.equal(receipt?.events?.[0]?.args?.[1], operator2.address); + + assert.equal(receipt?.events?.[1]?.event, "OwnableContractAccepted"); + assert.equal(receipt?.events?.[1]?.args?.[0], forwarder1.address); + + assert.equal(receipt?.events?.[2]?.event, "OwnershipTransferred"); + assert.equal(receipt?.events?.[2]?.address, forwarder2.address); + assert.equal(receipt?.events?.[2]?.args?.[0], operator.address); + assert.equal(receipt?.events?.[2]?.args?.[1], operator2.address); + + assert.equal(receipt?.events?.[3]?.event, "OwnableContractAccepted"); + assert.equal(receipt?.events?.[3]?.args?.[0], forwarder2.address); + }); + }); + + describe("being called by a non-owner authorized sender", () => { + it("does not revert", async () => { + await operator.connect(roles.defaultAccount).setAuthorizedSenders([await roles.oracleNode1.getAddress()]); + + await operator.connect(roles.oracleNode1).acceptOwnableContracts([]); + }); + }); + + describe("being called by a non owner", () => { + it("reverts with message", async () => { + await evmRevert( + operator.connect(roles.stranger).acceptOwnableContracts([await roles.oracleNode2.getAddress()]), + "Cannot set authorized senders", + ); + }); + }); + }); + + describe("#distributeFunds", () => { + describe("when called with empty arrays", () => { + it("reverts with invalid array message", async () => { + await evmRevert(operator.connect(roles.defaultAccount).distributeFunds([], []), "Invalid array length(s)"); + }); + }); + + describe("when called with unequal array lengths", () => { + it("reverts with invalid array message", async () => { + const receivers = [await roles.oracleNode2.getAddress(), await roles.oracleNode3.getAddress()]; + const amounts = [1, 2, 3]; + await evmRevert( + operator.connect(roles.defaultAccount).distributeFunds(receivers, amounts), + "Invalid array length(s)", + ); + }); + }); + + describe("when called with not enough ETH", () => { + it("reverts with subtraction overflow message", async () => { + const amountToSend = toWei("2"); + const ethSent = toWei("1"); + await evmRevert( + operator + .connect(roles.defaultAccount) + .distributeFunds([await roles.oracleNode2.getAddress()], [amountToSend], { + value: ethSent, + }), + "SafeMath: subtraction overflow", + ); + }); + }); + + describe("when called with too much ETH", () => { + it("reverts with too much ETH message", async () => { + const amountToSend = toWei("2"); + const ethSent = toWei("3"); + await evmRevert( + operator + .connect(roles.defaultAccount) + .distributeFunds([await roles.oracleNode2.getAddress()], [amountToSend], { + value: ethSent, + }), + "Too much ETH sent", + ); + }); + }); + + describe("when called with correct values", () => { + it("updates the balances", async () => { + const node2BalanceBefore = await roles.oracleNode2.getBalance(); + const node3BalanceBefore = await roles.oracleNode3.getBalance(); + const receivers = [await roles.oracleNode2.getAddress(), await roles.oracleNode3.getAddress()]; + const sendNode2 = toWei("2"); + const sendNode3 = toWei("3"); + const totalAmount = toWei("5"); + const amounts = [sendNode2, sendNode3]; + + await operator.connect(roles.defaultAccount).distributeFunds(receivers, amounts, { value: totalAmount }); + + const node2BalanceAfter = await roles.oracleNode2.getBalance(); + const node3BalanceAfter = await roles.oracleNode3.getBalance(); + + assert.equal(node2BalanceAfter.sub(node2BalanceBefore).toString(), sendNode2.toString()); + + assert.equal(node3BalanceAfter.sub(node3BalanceBefore).toString(), sendNode3.toString()); + }); + }); + }); + + describe("#setAuthorizedSenders", () => { + let newSenders: string[]; + let receipt: ContractReceipt; + describe("when called by the owner", () => { + describe("setting 3 authorized senders", () => { + beforeEach(async () => { + newSenders = [ + await roles.oracleNode1.getAddress(), + await roles.oracleNode2.getAddress(), + await roles.oracleNode3.getAddress(), + ]; + const tx = await operator.connect(roles.defaultAccount).setAuthorizedSenders(newSenders); + receipt = await tx.wait(); + }); + + it("adds the authorized nodes", async () => { + const authorizedSenders = await operator.getAuthorizedSenders(); + assert.equal(newSenders.length, authorizedSenders.length); + for (let i = 0; i < authorizedSenders.length; i++) { + assert.equal(authorizedSenders[i], newSenders[i]); + } + }); + + it("emits an event on the Operator", async () => { + assert.equal(receipt.events?.length, 1); + + const encodedSenders1 = ethers.utils.defaultAbiCoder.encode( + ["address[]", "address"], + [newSenders, await roles.defaultAccount.getAddress()], + ); + + const responseEvent1 = receipt.events?.[0]; + assert.equal(responseEvent1?.event, "AuthorizedSendersChanged"); + assert.equal(responseEvent1?.data, encodedSenders1); + }); + + it("replaces the authorized nodes", async () => { + const originalAuthorization = await operator + .connect(roles.defaultAccount) + .isAuthorizedSender(await roles.oracleNode.getAddress()); + assert.isFalse(originalAuthorization); + }); + + after(async () => { + await operator.connect(roles.defaultAccount).setAuthorizedSenders([await roles.oracleNode.getAddress()]); + }); + }); + + describe("setting 0 authorized senders", () => { + beforeEach(async () => { + newSenders = []; + }); + + it("reverts with a minimum senders message", async () => { + await evmRevert( + operator.connect(roles.defaultAccount).setAuthorizedSenders(newSenders), + "Must have at least 1 authorized sender", + ); + }); + }); + }); + + describe("when called by an authorized sender", () => { + beforeEach(async () => { + newSenders = [await roles.oracleNode1.getAddress()]; + await operator.connect(roles.defaultAccount).setAuthorizedSenders(newSenders); + }); + + it("succeeds", async () => { + await operator.connect(roles.defaultAccount).setAuthorizedSenders([await roles.stranger.getAddress()]); + }); + }); + + describe("when called by a non-owner", () => { + it("cannot add an authorized node", async () => { + await evmRevert( + operator.connect(roles.stranger).setAuthorizedSenders([await roles.stranger.getAddress()]), + "Cannot set authorized senders", + ); + }); + }); + }); + + describe("#setAuthorizedSendersOn", () => { + let newSenders: string[]; + + beforeEach(async () => { + await operator.connect(roles.defaultAccount).setAuthorizedSenders([await roles.oracleNode1.getAddress()]); + newSenders = [await roles.oracleNode2.getAddress(), await roles.oracleNode3.getAddress()]; + + forwarder1 = await forwarderFactory.connect(owner).deploy(link.address, operator.address, zeroAddress, "0x"); + forwarder2 = await forwarderFactory.connect(owner).deploy(link.address, operator.address, zeroAddress, "0x"); + }); + + describe("when called by a non-authorized sender", () => { + it("reverts", async () => { + await evmRevert( + operator.connect(roles.stranger).setAuthorizedSendersOn(newSenders, [forwarder1.address]), + "Cannot set authorized senders", + ); + }); + }); + + describe("when called by an owner", () => { + it("does not revert", async () => { + await operator + .connect(roles.defaultAccount) + .setAuthorizedSendersOn([forwarder1.address, forwarder2.address], newSenders); + }); + }); + + describe("when called by an authorized sender", () => { + it("does not revert", async () => { + await operator + .connect(roles.oracleNode1) + .setAuthorizedSendersOn([forwarder1.address, forwarder2.address], newSenders); + }); + + it("does revert with 0 senders", async () => { + await operator + .connect(roles.oracleNode1) + .setAuthorizedSendersOn([forwarder1.address, forwarder2.address], newSenders); + }); + + it("emits a log announcing the change and who made it", async () => { + const targets = [forwarder1.address, forwarder2.address]; + const tx = await operator.connect(roles.oracleNode1).setAuthorizedSendersOn(targets, newSenders); + + const receipt = await tx.wait(); + const encodedArgs = ethers.utils.defaultAbiCoder.encode( + ["address[]", "address[]", "address"], + [targets, newSenders, await roles.oracleNode1.getAddress()], + ); + + const event1 = receipt.events?.[0]; + assert.equal(event1?.event, "TargetsUpdatedAuthorizedSenders"); + assert.equal(event1?.address, operator.address); + assert.equal(event1?.data, encodedArgs); + }); + + it("updates the sender list on each of the targets", async () => { + const tx = await operator + .connect(roles.oracleNode1) + .setAuthorizedSendersOn([forwarder1.address, forwarder2.address], newSenders); + + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 3, receipt.toString()); + const encodedSenders = ethers.utils.defaultAbiCoder.encode( + ["address[]", "address"], + [newSenders, operator.address], + ); + + const event1 = receipt.events?.[1]; + assert.equal(event1?.event, "AuthorizedSendersChanged"); + assert.equal(event1?.address, forwarder1.address); + assert.equal(event1?.data, encodedSenders); + + const event2 = receipt.events?.[2]; + assert.equal(event2?.event, "AuthorizedSendersChanged"); + assert.equal(event2?.address, forwarder2.address); + assert.equal(event2?.data, encodedSenders); + }); + }); + }); + + describe("#acceptAuthorizedReceivers", () => { + let newSenders: string[]; + + describe("being called by the owner", () => { + let operator2: Contract; + let receipt: ContractReceipt; + + beforeEach(async () => { + operator2 = await operatorFactory + .connect(roles.defaultAccount) + .deploy(link.address, await roles.defaultAccount.getAddress()); + forwarder1 = await forwarderFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, zeroAddress, "0x"); + forwarder2 = await forwarderFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, zeroAddress, "0x"); + await operator + .connect(roles.defaultAccount) + .transferOwnableContracts([forwarder1.address, forwarder2.address], operator2.address); + newSenders = [await roles.oracleNode2.getAddress(), await roles.oracleNode3.getAddress()]; + + const tx = await operator2 + .connect(roles.defaultAccount) + .acceptAuthorizedReceivers([forwarder1.address, forwarder2.address], newSenders); + receipt = await tx.wait(); + }); + + it("sets the new owner on the forwarder", async () => { + assert.equal(await forwarder1.owner(), operator2.address); + }); + + it("emits ownership transferred events", async () => { + assert.equal(receipt?.events?.[0]?.event, "OwnershipTransferred"); + assert.equal(receipt?.events?.[0]?.address, forwarder1.address); + assert.equal(receipt?.events?.[0]?.args?.[0], operator.address); + assert.equal(receipt?.events?.[0]?.args?.[1], operator2.address); + + assert.equal(receipt?.events?.[1]?.event, "OwnableContractAccepted"); + assert.equal(receipt?.events?.[1]?.args?.[0], forwarder1.address); + + assert.equal(receipt?.events?.[2]?.event, "OwnershipTransferred"); + assert.equal(receipt?.events?.[2]?.address, forwarder2.address); + assert.equal(receipt?.events?.[2]?.args?.[0], operator.address); + assert.equal(receipt?.events?.[2]?.args?.[1], operator2.address); + + assert.equal(receipt?.events?.[3]?.event, "OwnableContractAccepted"); + assert.equal(receipt?.events?.[3]?.args?.[0], forwarder2.address); + + assert.equal(receipt?.events?.[4]?.event, "TargetsUpdatedAuthorizedSenders"); + + const encodedSenders = ethers.utils.defaultAbiCoder.encode( + ["address[]", "address"], + [newSenders, operator2.address], + ); + assert.equal(receipt?.events?.[5]?.event, "AuthorizedSendersChanged"); + assert.equal(receipt?.events?.[5]?.address, forwarder1.address); + assert.equal(receipt?.events?.[5]?.data, encodedSenders); + + assert.equal(receipt?.events?.[6]?.event, "AuthorizedSendersChanged"); + assert.equal(receipt?.events?.[6]?.address, forwarder2.address); + assert.equal(receipt?.events?.[6]?.data, encodedSenders); + }); + }); + + describe("being called by a non owner", () => { + it("reverts with message", async () => { + await evmRevert( + operator + .connect(roles.stranger) + .acceptAuthorizedReceivers([forwarder1.address, forwarder2.address], newSenders), + "Cannot set authorized senders", + ); + }); + }); + }); + + describe("#onTokenTransfer", () => { + describe("when called from any address but the LINK token", () => { + it("triggers the intended method", async () => { + const callData = encodeOracleRequest(specId, to, fHash, 0, constants.HashZero); + + await evmRevert(operator.onTokenTransfer(await roles.defaultAccount.getAddress(), 0, callData)); + }); + }); + + describe("when called from the LINK token", () => { + it("triggers the intended method", async () => { + const callData = encodeOracleRequest(specId, to, fHash, 0, constants.HashZero); + + const tx = await link.transferAndCall(operator.address, 0, callData, { + value: 0, + }); + const receipt = await tx.wait(); + + assert.equal(3, receipt.logs?.length); + }); + + describe("with no data", () => { + it("reverts", async () => { + await evmRevert( + link.transferAndCall(operator.address, 0, "0x", { + value: 0, + }), + ); + }); + }); + }); + + describe("malicious requester", () => { + let mock: Contract; + let requester: Contract; + const paymentAmount = toWei("1"); + + beforeEach(async () => { + mock = await maliciousRequesterFactory.connect(roles.defaultAccount).deploy(link.address, operator.address); + await link.transfer(mock.address, paymentAmount); + }); + + it("cannot withdraw from oracle", async () => { + const operatorOriginalBalance = await link.balanceOf(operator.address); + const mockOriginalBalance = await link.balanceOf(mock.address); + + await evmRevert(mock.maliciousWithdraw()); + + const operatorNewBalance = await link.balanceOf(operator.address); + const mockNewBalance = await link.balanceOf(mock.address); + + bigNumEquals(operatorOriginalBalance, operatorNewBalance); + bigNumEquals(mockNewBalance, mockOriginalBalance); + }); + + describe("if the requester tries to create a requestId for another contract", () => { + it("the requesters ID will not match with the oracle contract", async () => { + const tx = await mock.maliciousTargetConsumer(to); + const receipt = await tx.wait(); + + const mockRequestId = receipt.logs?.[0].data; + const requestId = (receipt.events?.[0].args as any).requestId; + assert.notEqual(mockRequestId, requestId); + }); + + it("the target requester can still create valid requests", async () => { + requester = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, specId); + await link.transfer(requester.address, paymentAmount); + await mock.maliciousTargetConsumer(requester.address); + await requester.requestEthereumPrice("USD", paymentAmount); + }); + }); + }); + + it("does not allow recursive calls of onTokenTransfer", async () => { + const requestPayload = encodeOracleRequest(specId, to, fHash, 0, constants.HashZero); + + const ottSelector = operatorFactory.interface.getSighash("onTokenTransfer"); + const header = + "000000000000000000000000c5fdf4076b8f3a5357c5e395ab970b5b54098fef" + // to + "0000000000000000000000000000000000000000000000000000000000000539" + // amount + "0000000000000000000000000000000000000000000000000000000000000060" + // offset + "0000000000000000000000000000000000000000000000000000000000000136"; // length + + const maliciousPayload = ottSelector + header + requestPayload.slice(2); + + await evmRevert( + link.transferAndCall(operator.address, 0, maliciousPayload, { + value: 0, + }), + ); + }); + }); + + describe("#oracleRequest", () => { + describe("when called through the LINK token", () => { + const paid = 100; + let log: providers.Log | undefined; + let receipt: providers.TransactionReceipt; + + beforeEach(async () => { + const args = encodeOracleRequest(specId, to, fHash, 1, constants.HashZero); + const tx = await link.transferAndCall(operator.address, paid, args); + receipt = await tx.wait(); + assert.equal(3, receipt?.logs?.length); + + log = receipt.logs && receipt.logs[2]; + }); + + it("logs an event", async () => { + assert.equal(operator.address, log?.address); + + assert.equal(log?.topics?.[1], specId); + + const req = decodeRunRequest(receipt?.logs?.[2]); + assert.equal(await roles.defaultAccount.getAddress(), req.requester); + bigNumEquals(paid, req.payment); + }); + + it("uses the expected event signature", async () => { + // If updating this test, be sure to update models.RunLogTopic. + const eventSignature = "0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65"; + assert.equal(eventSignature, log?.topics?.[0]); + }); + + it("does not allow the same requestId to be used twice", async () => { + const args2 = encodeOracleRequest(specId, to, fHash, 1, constants.HashZero); + await evmRevert(link.transferAndCall(operator.address, paid, args2)); + }); + + describe("when called with a payload less than 2 EVM words + function selector", () => { + it("throws an error", async () => { + const funcSelector = operatorFactory.interface.getSighash("oracleRequest"); + const maliciousData = funcSelector + "0000000000000000000000000000000000000000000000000000000000000000000"; + await evmRevert(link.transferAndCall(operator.address, paid, maliciousData)); + }); + }); + + describe("when called with a payload between 3 and 9 EVM words", () => { + it("throws an error", async () => { + const funcSelector = operatorFactory.interface.getSighash("oracleRequest"); + const maliciousData = + funcSelector + + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"; + await evmRevert(link.transferAndCall(operator.address, paid, maliciousData)); + }); + }); + }); + + describe("when dataVersion is higher than 255", () => { + it("throws an error", async () => { + const paid = 100; + const args = encodeOracleRequest(specId, to, fHash, 1, constants.HashZero, 256); + await evmRevert(link.transferAndCall(operator.address, paid, args)); + }); + }); + + describe("when not called through the LINK token", () => { + it("reverts", async () => { + await evmRevert( + operator + .connect(roles.oracleNode) + .oracleRequest("0x0000000000000000000000000000000000000000", 0, specId, to, fHash, 1, 1, "0x"), + ); + }); + }); + }); + + describe("#requestOracleData", () => { + describe("when called through the LINK token", () => { + const paid = 100; + let log: providers.Log | undefined; + let receipt: providers.TransactionReceipt; + + beforeEach(async () => { + const args = encodeRequestOracleData(specId, to, fHash, 1, constants.HashZero); + const tx = await link.transferAndCall(operator.address, paid, args); + receipt = await tx.wait(); + assert.equal(3, receipt?.logs?.length); + + log = receipt.logs && receipt.logs[2]; + }); + + it("logs an event", async () => { + assert.equal(operator.address, log?.address); + + assert.equal(log?.topics?.[1], specId); + + const req = decodeRunRequest(receipt?.logs?.[2]); + assert.equal(await roles.defaultAccount.getAddress(), req.requester); + bigNumEquals(paid, req.payment); + }); + + it("uses the expected event signature", async () => { + // If updating this test, be sure to update models.RunLogTopic. + const eventSignature = "0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65"; + assert.equal(eventSignature, log?.topics?.[0]); + }); + + it("does not allow the same requestId to be used twice", async () => { + const args2 = encodeRequestOracleData(specId, to, fHash, 1, constants.HashZero); + await evmRevert(link.transferAndCall(operator.address, paid, args2)); + }); + + describe("when called with a payload less than 2 EVM words + function selector", () => { + it("throws an error", async () => { + const funcSelector = operatorFactory.interface.getSighash("oracleRequest"); + const maliciousData = funcSelector + "0000000000000000000000000000000000000000000000000000000000000000000"; + await evmRevert(link.transferAndCall(operator.address, paid, maliciousData)); + }); + }); + + describe("when called with a payload between 3 and 9 EVM words", () => { + it("throws an error", async () => { + const funcSelector = operatorFactory.interface.getSighash("oracleRequest"); + const maliciousData = + funcSelector + + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"; + await evmRevert(link.transferAndCall(operator.address, paid, maliciousData)); + }); + }); + }); + + describe("when dataVersion is higher than 255", () => { + it("throws an error", async () => { + const paid = 100; + const args = encodeRequestOracleData(specId, to, fHash, 1, constants.HashZero, 256); + await evmRevert(link.transferAndCall(operator.address, paid, args)); + }); + }); + + describe("when not called through the LINK token", () => { + it("reverts", async () => { + await evmRevert( + operator + .connect(roles.oracleNode) + .oracleRequest("0x0000000000000000000000000000000000000000", 0, specId, to, fHash, 1, 1, "0x"), + ); + }); + }); + }); + + describe("#fulfillOracleRequest", () => { + const response = "Hi Mom!"; + let maliciousRequester: Contract; + let basicConsumer: Contract; + let maliciousConsumer: Contract; + let gasGuzzlingConsumer: Contract; + let request: ReturnType; + + describe("gas guzzling consumer", () => { + beforeEach(async () => { + gasGuzzlingConsumer = await gasGuzzlingConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(gasGuzzlingConsumer.address, paymentAmount); + const tx = await gasGuzzlingConsumer.gassyRequestEthereumPrice(paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("emits an OracleResponse event", async () => { + const fulfillParams = convertFufillParams(request, response); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 1); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + }); + + describe("cooperative consumer", () => { + beforeEach(async () => { + basicConsumer = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(basicConsumer.address, paymentAmount); + const currency = "USD"; + const tx = await basicConsumer.requestEthereumPrice(currency, paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + describe("when called by an unauthorized node", () => { + beforeEach(async () => { + assert.equal(false, await operator.isAuthorizedSender(await roles.stranger.getAddress())); + }); + + it("raises an error", async () => { + await evmRevert( + operator.connect(roles.stranger).fulfillOracleRequest(...convertFufillParams(request, response)), + ); + }); + }); + + describe("when fulfilled with the wrong function", () => { + let v7Consumer; + beforeEach(async () => { + v7Consumer = await v7ConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(v7Consumer.address, paymentAmount); + const currency = "USD"; + const tx = await v7Consumer.requestEthereumPrice(currency, paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("raises an error", async () => { + await evmRevert( + operator.connect(roles.stranger).fulfillOracleRequest(...convertFufillParams(request, response)), + ); + }); + }); + + describe("when called by an authorized node", () => { + it("raises an error if the request ID does not exist", async () => { + request.requestId = ethers.utils.formatBytes32String("DOESNOTEXIST"); + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)), + ); + }); + + it("sets the value on the requested contract", async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + const currentValue = await basicConsumer.currentPrice(); + assert.equal(response, ethers.utils.parseBytes32String(currentValue)); + }); + + it("emits an OracleResponse event", async () => { + const fulfillParams = convertFufillParams(request, response); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 3); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + + it("does not allow a request to be fulfilled twice", async () => { + const response2 = response + " && Hello World!!"; + + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response2)), + ); + + const currentValue = await basicConsumer.currentPrice(); + assert.equal(response, ethers.utils.parseBytes32String(currentValue)); + }); + }); + + describe("when the oracle does not provide enough gas", () => { + // if updating this defaultGasLimit, be sure it matches with the + // defaultGasLimit specified in store/tx_manager.go + const defaultGasLimit = 500000; + + beforeEach(async () => { + bigNumEquals(0, await operator.withdrawable()); + }); + + it("does not allow the oracle to withdraw the payment", async () => { + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest( + ...convertFufillParams(request, response, { + gasLimit: 70000, + }), + ), + ); + + bigNumEquals(0, await operator.withdrawable()); + }); + + it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest( + ...convertFufillParams(request, response, { + gasLimit: defaultGasLimit, + }), + ); + + bigNumEquals(request.payment, await operator.withdrawable()); + }); + }); + }); + + describe("with a malicious requester", () => { + beforeEach(async () => { + const paymentAmount = toWei("1"); + maliciousRequester = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousRequester.address, paymentAmount); + }); + + it("cannot cancel before the expiration", async () => { + await evmRevert( + maliciousRequester.maliciousRequestCancel(specId, ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)")), + ); + }); + + it("cannot call functions on the LINK token through callbacks", async () => { + await evmRevert( + maliciousRequester.request(specId, link.address, ethers.utils.toUtf8Bytes("transfer(address,uint256)")), + ); + }); + + describe("requester lies about amount of LINK sent", () => { + it("the oracle uses the amount of LINK actually paid", async () => { + const tx = await maliciousRequester.maliciousPrice(specId); + const receipt = await tx.wait(); + const req = decodeRunRequest(receipt.logs?.[3]); + + assert(toWei("1").eq(req.payment)); + }); + }); + }); + + describe("with a malicious consumer", () => { + const paymentAmount = toWei("1"); + + beforeEach(async () => { + maliciousConsumer = await maliciousConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousConsumer.address, paymentAmount); + }); + + describe("fails during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("assertFail(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response2 = "hack the planet 102"; + + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response2)), + ); + }); + }); + + describe("calls selfdestruct", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + await maliciousConsumer.remove(); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + }); + + describe("request is canceled during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("cancelRequestOnFulfill(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + const mockBalance = await link.balanceOf(maliciousConsumer.address); + bigNumEquals(mockBalance, 0); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response2 = "hack the planet 102"; + + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response2)), + ); + }); + }); + + describe("tries to steal funds from node", () => { + it("is not successful with call", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthCall(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with send", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthSend(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with transfer", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthTransfer(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, response)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + }); + }); + }); + + describe("#fulfillOracleRequest2", () => { + describe("single word fulfils", () => { + const response = "Hi mom!"; + const responseTypes = ["bytes32"]; + const responseValues = [toBytes32String(response)]; + let maliciousRequester: Contract; + let basicConsumer: Contract; + let maliciousConsumer: Contract; + let gasGuzzlingConsumer: Contract; + let request: ReturnType; + + describe("gas guzzling consumer", () => { + beforeEach(async () => { + gasGuzzlingConsumer = await gasGuzzlingConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(gasGuzzlingConsumer.address, paymentAmount); + const tx = await gasGuzzlingConsumer.gassyRequestEthereumPrice(paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("emits an OracleResponse2 event", async () => { + const fulfillParams = convertFulfill2Params(request, responseTypes, responseValues); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest2(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 1); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + }); + + describe("cooperative consumer", () => { + beforeEach(async () => { + basicConsumer = await basicConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(basicConsumer.address, paymentAmount); + const currency = "USD"; + const tx = await basicConsumer.requestEthereumPrice(currency, paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + describe("when called by an unauthorized node", () => { + beforeEach(async () => { + assert.equal(false, await operator.isAuthorizedSender(await roles.stranger.getAddress())); + }); + + it("raises an error", async () => { + await evmRevert( + operator + .connect(roles.stranger) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)), + ); + }); + }); + + describe("when called by an authorized node", () => { + it("raises an error if the request ID does not exist", async () => { + request.requestId = ethers.utils.formatBytes32String("DOESNOTEXIST"); + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)), + ); + }); + + it("sets the value on the requested contract", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const currentValue = await basicConsumer.currentPrice(); + assert.equal(response, ethers.utils.parseBytes32String(currentValue)); + }); + + it("emits an OracleResponse2 event", async () => { + const fulfillParams = convertFulfill2Params(request, responseTypes, responseValues); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest2(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 3); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + + it("does not allow a request to be fulfilled twice", async () => { + const response2 = response + " && Hello World!!"; + const response2Values = [toBytes32String(response2)]; + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, response2Values)), + ); + + const currentValue = await basicConsumer.currentPrice(); + assert.equal(response, ethers.utils.parseBytes32String(currentValue)); + }); + }); + + describe("when the oracle does not provide enough gas", () => { + // if updating this defaultGasLimit, be sure it matches with the + // defaultGasLimit specified in store/tx_manager.go + const defaultGasLimit = 500000; + + beforeEach(async () => { + bigNumEquals(0, await operator.withdrawable()); + }); + + it("does not allow the oracle to withdraw the payment", async () => { + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest2( + ...convertFulfill2Params(request, responseTypes, responseValues, { + gasLimit: 70000, + }), + ), + ); + + bigNumEquals(0, await operator.withdrawable()); + }); + + it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest2( + ...convertFulfill2Params(request, responseTypes, responseValues, { + gasLimit: defaultGasLimit, + }), + ); + + bigNumEquals(request.payment, await operator.withdrawable()); + }); + }); + }); + + describe("with a malicious requester", () => { + beforeEach(async () => { + const paymentAmount = toWei("1"); + maliciousRequester = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousRequester.address, paymentAmount); + }); + + it("cannot cancel before the expiration", async () => { + await evmRevert( + maliciousRequester.maliciousRequestCancel(specId, ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)")), + ); + }); + + it("cannot call functions on the LINK token through callbacks", async () => { + await evmRevert( + maliciousRequester.request(specId, link.address, ethers.utils.toUtf8Bytes("transfer(address,uint256)")), + ); + }); + + describe("requester lies about amount of LINK sent", () => { + it("the oracle uses the amount of LINK actually paid", async () => { + const tx = await maliciousRequester.maliciousPrice(specId); + const receipt = await tx.wait(); + const req = decodeRunRequest(receipt.logs?.[3]); + + assert(toWei("1").eq(req.payment)); + }); + }); + }); + + describe("with a malicious consumer", () => { + const paymentAmount = toWei("1"); + + beforeEach(async () => { + maliciousConsumer = await maliciousConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousConsumer.address, paymentAmount); + }); + + describe("fails during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("assertFail(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response2 = "hack the planet 102"; + const response2Values = [toBytes32String(response2)]; + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, response2Values)), + ); + }); + }); + + describe("calls selfdestruct", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + await maliciousConsumer.remove(); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + }); + + describe("request is canceled during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("cancelRequestOnFulfill(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const mockBalance = await link.balanceOf(maliciousConsumer.address); + bigNumEquals(mockBalance, 0); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response2 = "hack the planet 102"; + const response2Values = [toBytes32String(response2)]; + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, response2Values)), + ); + }); + }); + + describe("tries to steal funds from node", () => { + it("is not successful with call", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthCall(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with send", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthSend(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with transfer", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthTransfer(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + }); + }); + }); + + describe("multi word fulfils", () => { + describe("one bytes parameter", () => { + const response = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\ + Fusce euismod malesuada ligula, eget semper metus ultrices sit amet."; + const responseTypes = ["bytes"]; + const responseValues = [stringToBytes(response)]; + let maliciousRequester: Contract; + let multiConsumer: Contract; + let maliciousConsumer: Contract; + let gasGuzzlingConsumer: Contract; + let request: ReturnType; + + describe("gas guzzling consumer", () => { + beforeEach(async () => { + gasGuzzlingConsumer = await gasGuzzlingConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(gasGuzzlingConsumer.address, paymentAmount); + const tx = await gasGuzzlingConsumer.gassyMultiWordRequest(paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("emits an OracleResponse2 event", async () => { + const fulfillParams = convertFulfill2Params(request, responseTypes, responseValues); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest2(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 1); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + }); + + describe("cooperative consumer", () => { + beforeEach(async () => { + multiConsumer = await multiWordConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(multiConsumer.address, paymentAmount); + const currency = "USD"; + const tx = await multiConsumer.requestEthereumPrice(currency, paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + describe("when called by an unauthorized node", () => { + beforeEach(async () => { + assert.equal(false, await operator.isAuthorizedSender(await roles.stranger.getAddress())); + }); + + it("raises an error", async () => { + await evmRevert( + operator + .connect(roles.stranger) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)), + ); + }); + }); + + describe("when called by an authorized node", () => { + it("raises an error if the request ID does not exist", async () => { + request.requestId = ethers.utils.formatBytes32String("DOESNOTEXIST"); + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)), + ); + }); + + it("sets the value on the requested contract", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const currentValue = await multiConsumer.currentPrice(); + assert.equal(response, ethers.utils.toUtf8String(currentValue)); + }); + + it("emits an OracleResponse2 event", async () => { + const fulfillParams = convertFulfill2Params(request, responseTypes, responseValues); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest2(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 3); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + + it("does not allow a request to be fulfilled twice", async () => { + const response2 = response + " && Hello World!!"; + const response2Values = [stringToBytes(response2)]; + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, response2Values)), + ); + + const currentValue = await multiConsumer.currentPrice(); + assert.equal(response, ethers.utils.toUtf8String(currentValue)); + }); + }); + + describe("when the oracle does not provide enough gas", () => { + // if updating this defaultGasLimit, be sure it matches with the + // defaultGasLimit specified in store/tx_manager.go + const defaultGasLimit = 500000; + + beforeEach(async () => { + bigNumEquals(0, await operator.withdrawable()); + }); + + it("does not allow the oracle to withdraw the payment", async () => { + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest2( + ...convertFulfill2Params(request, responseTypes, responseValues, { + gasLimit: 70000, + }), + ), + ); + + bigNumEquals(0, await operator.withdrawable()); + }); + + it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest2( + ...convertFulfill2Params(request, responseTypes, responseValues, { + gasLimit: defaultGasLimit, + }), + ); + + bigNumEquals(request.payment, await operator.withdrawable()); + }); + }); + }); + + describe("with a malicious requester", () => { + beforeEach(async () => { + const paymentAmount = toWei("1"); + maliciousRequester = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousRequester.address, paymentAmount); + }); + + it("cannot cancel before the expiration", async () => { + await evmRevert( + maliciousRequester.maliciousRequestCancel( + specId, + ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)"), + ), + ); + }); + + it("cannot call functions on the LINK token through callbacks", async () => { + await evmRevert( + maliciousRequester.request(specId, link.address, ethers.utils.toUtf8Bytes("transfer(address,uint256)")), + ); + }); + + describe("requester lies about amount of LINK sent", () => { + it("the oracle uses the amount of LINK actually paid", async () => { + const tx = await maliciousRequester.maliciousPrice(specId); + const receipt = await tx.wait(); + const req = decodeRunRequest(receipt.logs?.[3]); + + assert(toWei("1").eq(req.payment)); + }); + }); + }); + + describe("with a malicious consumer", () => { + const paymentAmount = toWei("1"); + + beforeEach(async () => { + maliciousConsumer = await maliciousMultiWordConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousConsumer.address, paymentAmount); + }); + + describe("fails during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("assertFail(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response2 = "hack the planet 102"; + const response2Values = [stringToBytes(response2)]; + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, response2Values)), + ); + }); + }); + + describe("calls selfdestruct", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + await maliciousConsumer.remove(); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + }); + + describe("request is canceled during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("cancelRequestOnFulfill(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const mockBalance = await link.balanceOf(maliciousConsumer.address); + bigNumEquals(mockBalance, 0); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response2 = "hack the planet 102"; + const response2Values = [stringToBytes(response2)]; + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, response2Values)), + ); + }); + }); + + describe("tries to steal funds from node", () => { + it("is not successful with call", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthCall(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with send", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthSend(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with transfer", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthTransfer(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + }); + }); + }); + + describe("multiple bytes32 parameters", () => { + const response1 = "Hi mom!"; + const response2 = "Its me!"; + const responseTypes = ["bytes32", "bytes32"]; + const responseValues = [toBytes32String(response1), toBytes32String(response2)]; + let maliciousRequester: Contract; + let multiConsumer: Contract; + let maliciousConsumer: Contract; + let gasGuzzlingConsumer: Contract; + let request: ReturnType; + + describe("gas guzzling consumer", () => { + beforeEach(async () => { + gasGuzzlingConsumer = await gasGuzzlingConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(gasGuzzlingConsumer.address, paymentAmount); + const tx = await gasGuzzlingConsumer.gassyMultiWordRequest(paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("emits an OracleResponse2 event", async () => { + const fulfillParams = convertFulfill2Params(request, responseTypes, responseValues); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest2(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 1); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + }); + + describe("cooperative consumer", () => { + beforeEach(async () => { + multiConsumer = await multiWordConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address, specId); + const paymentAmount = toWei("1"); + await link.transfer(multiConsumer.address, paymentAmount); + const currency = "USD"; + const tx = await multiConsumer.requestMultipleParameters(currency, paymentAmount); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + describe("when called by an unauthorized node", () => { + beforeEach(async () => { + assert.equal(false, await operator.isAuthorizedSender(await roles.stranger.getAddress())); + }); + + it("raises an error", async () => { + await evmRevert( + operator + .connect(roles.stranger) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)), + ); + }); + }); + + describe("when called by an authorized node", () => { + it("raises an error if the request ID does not exist", async () => { + request.requestId = ethers.utils.formatBytes32String("DOESNOTEXIST"); + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)), + ); + }); + + it("sets the value on the requested contract", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const firstValue = await multiConsumer.first(); + const secondValue = await multiConsumer.second(); + assert.equal(response1, ethers.utils.parseBytes32String(firstValue)); + assert.equal(response2, ethers.utils.parseBytes32String(secondValue)); + }); + + it("emits an OracleResponse2 event", async () => { + const fulfillParams = convertFulfill2Params(request, responseTypes, responseValues); + const tx = await operator.connect(roles.oracleNode).fulfillOracleRequest2(...fulfillParams); + const receipt = await tx.wait(); + assert.equal(receipt.events?.length, 3); + const responseEvent = receipt.events?.[0]; + assert.equal(responseEvent?.event, "OracleResponse"); + assert.equal(responseEvent?.args?.[0], request.requestId); + }); + + it("does not allow a request to be fulfilled twice", async () => { + const response3 = response2 + " && Hello World!!"; + const repeatedResponseValues = [toBytes32String(response2), toBytes32String(response3)]; + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, repeatedResponseValues)), + ); + + const firstValue = await multiConsumer.first(); + const secondValue = await multiConsumer.second(); + assert.equal(response1, ethers.utils.parseBytes32String(firstValue)); + assert.equal(response2, ethers.utils.parseBytes32String(secondValue)); + }); + }); + + describe("when the oracle does not provide enough gas", () => { + // if updating this defaultGasLimit, be sure it matches with the + // defaultGasLimit specified in store/tx_manager.go + const defaultGasLimit = 500000; + + beforeEach(async () => { + bigNumEquals(0, await operator.withdrawable()); + }); + + it("does not allow the oracle to withdraw the payment", async () => { + await evmRevert( + operator.connect(roles.oracleNode).fulfillOracleRequest2( + ...convertFulfill2Params(request, responseTypes, responseValues, { + gasLimit: 70000, + }), + ), + ); + + bigNumEquals(0, await operator.withdrawable()); + }); + + it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { + await operator.connect(roles.oracleNode).fulfillOracleRequest2( + ...convertFulfill2Params(request, responseTypes, responseValues, { + gasLimit: defaultGasLimit, + }), + ); + + bigNumEquals(request.payment, await operator.withdrawable()); + }); + }); + }); + + describe("with a malicious requester", () => { + beforeEach(async () => { + const paymentAmount = toWei("1"); + maliciousRequester = await maliciousRequesterFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousRequester.address, paymentAmount); + }); + + it("cannot cancel before the expiration", async () => { + await evmRevert( + maliciousRequester.maliciousRequestCancel( + specId, + ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)"), + ), + ); + }); + + it("cannot call functions on the LINK token through callbacks", async () => { + await evmRevert( + maliciousRequester.request(specId, link.address, ethers.utils.toUtf8Bytes("transfer(address,uint256)")), + ); + }); + + describe("requester lies about amount of LINK sent", () => { + it("the oracle uses the amount of LINK actually paid", async () => { + const tx = await maliciousRequester.maliciousPrice(specId); + const receipt = await tx.wait(); + const req = decodeRunRequest(receipt.logs?.[3]); + + assert(toWei("1").eq(req.payment)); + }); + }); + }); + + describe("with a malicious consumer", () => { + const paymentAmount = toWei("1"); + + beforeEach(async () => { + maliciousConsumer = await maliciousMultiWordConsumerFactory + .connect(roles.defaultAccount) + .deploy(link.address, operator.address); + await link.transfer(maliciousConsumer.address, paymentAmount); + }); + + describe("fails during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("assertFail(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response3 = "hack the planet 102"; + const repeatedResponseValues = [toBytes32String(response2), toBytes32String(response3)]; + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, repeatedResponseValues)), + ); + }); + }); + + describe("calls selfdestruct", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("doesNothing(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + await maliciousConsumer.remove(); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + }); + + describe("request is canceled during fulfillment", () => { + beforeEach(async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("cancelRequestOnFulfill(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)); + }); + + it("allows the oracle node to receive their payment", async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + const mockBalance = await link.balanceOf(maliciousConsumer.address); + bigNumEquals(mockBalance, 0); + + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(balance, 0); + + await operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), paymentAmount); + const newBalance = await link.balanceOf(await roles.oracleNode.getAddress()); + bigNumEquals(paymentAmount, newBalance); + }); + + it("can't fulfill the data again", async () => { + const response3 = "hack the planet 102"; + const repeatedResponseValues = [toBytes32String(response2), toBytes32String(response3)]; + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + await evmRevert( + operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, repeatedResponseValues)), + ); + }); + }); + + describe("tries to steal funds from node", () => { + it("is not successful with call", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthCall(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with send", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthSend(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + + it("is not successful with transfer", async () => { + const tx = await maliciousConsumer.requestData( + specId, + ethers.utils.toUtf8Bytes("stealEthTransfer(bytes32,bytes32)"), + ); + const receipt = await tx.wait(); + request = decodeRunRequest(receipt.logs?.[3]); + + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request, responseTypes, responseValues)); + bigNumEquals(0, await ethers.provider.getBalance(maliciousConsumer.address)); + }); + }); + }); + }); + }); + }); + + describe("#withdraw", () => { + describe("without reserving funds via oracleRequest", () => { + it("does nothing", async () => { + let balance = await link.balanceOf(await roles.oracleNode.getAddress()); + assert.equal(0, balance.toNumber()); + await evmRevert( + operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), toWei("1")), + ); + balance = await link.balanceOf(await roles.oracleNode.getAddress()); + assert.equal(0, balance.toNumber()); + }); + + describe("recovering funds that were mistakenly sent", () => { + const paid = 1; + beforeEach(async () => { + await link.transfer(operator.address, paid); + }); + + it("withdraws funds", async () => { + const operatorBalanceBefore = await link.balanceOf(operator.address); + const accountBalanceBefore = await link.balanceOf(await roles.defaultAccount.getAddress()); + + await operator.connect(roles.defaultAccount).withdraw(await roles.defaultAccount.getAddress(), paid); + + const operatorBalanceAfter = await link.balanceOf(operator.address); + const accountBalanceAfter = await link.balanceOf(await roles.defaultAccount.getAddress()); + + const accountDifference = accountBalanceAfter.sub(accountBalanceBefore); + const operatorDifference = operatorBalanceBefore.sub(operatorBalanceAfter); + + bigNumEquals(operatorDifference, paid); + bigNumEquals(accountDifference, paid); + }); + }); + }); + + describe("reserving funds via oracleRequest", () => { + const payment = 15; + let request: ReturnType; + + beforeEach(async () => { + const mock = await getterSetterFactory.connect(roles.defaultAccount).deploy(); + const args = encodeOracleRequest(specId, mock.address, fHash, 0, constants.HashZero); + const tx = await link.transferAndCall(operator.address, payment, args); + const receipt = await tx.wait(); + assert.equal(3, receipt.logs?.length); + request = decodeRunRequest(receipt.logs?.[2]); + }); + + describe("but not freeing funds w fulfillOracleRequest", () => { + it("does not transfer funds", async () => { + await evmRevert( + operator.connect(roles.defaultAccount).withdraw(await roles.oracleNode.getAddress(), payment), + ); + const balance = await link.balanceOf(await roles.oracleNode.getAddress()); + assert.equal(0, balance.toNumber()); + }); + + describe("recovering funds that were mistakenly sent", () => { + const paid = 1; + beforeEach(async () => { + await link.transfer(operator.address, paid); + }); + + it("withdraws funds", async () => { + const operatorBalanceBefore = await link.balanceOf(operator.address); + const accountBalanceBefore = await link.balanceOf(await roles.defaultAccount.getAddress()); + + await operator.connect(roles.defaultAccount).withdraw(await roles.defaultAccount.getAddress(), paid); + + const operatorBalanceAfter = await link.balanceOf(operator.address); + const accountBalanceAfter = await link.balanceOf(await roles.defaultAccount.getAddress()); + + const accountDifference = accountBalanceAfter.sub(accountBalanceBefore); + const operatorDifference = operatorBalanceBefore.sub(operatorBalanceAfter); + + bigNumEquals(operatorDifference, paid); + bigNumEquals(accountDifference, paid); + }); + }); + }); + + describe("and freeing funds", () => { + beforeEach(async () => { + await operator + .connect(roles.oracleNode) + .fulfillOracleRequest(...convertFufillParams(request, "Hello World!")); + }); + + it("does not allow input greater than the balance", async () => { + const originalOracleBalance = await link.balanceOf(operator.address); + const originalStrangerBalance = await link.balanceOf(await roles.stranger.getAddress()); + const withdrawalAmount = payment + 1; + + assert.isAbove(withdrawalAmount, originalOracleBalance.toNumber()); + await evmRevert( + operator.connect(roles.defaultAccount).withdraw(await roles.stranger.getAddress(), withdrawalAmount), + ); + + const newOracleBalance = await link.balanceOf(operator.address); + const newStrangerBalance = await link.balanceOf(await roles.stranger.getAddress()); + + assert.equal(originalOracleBalance.toNumber(), newOracleBalance.toNumber()); + assert.equal(originalStrangerBalance.toNumber(), newStrangerBalance.toNumber()); + }); + + it("allows transfer of partial balance by owner to specified address", async () => { + const partialAmount = 6; + const difference = payment - partialAmount; + await operator.connect(roles.defaultAccount).withdraw(await roles.stranger.getAddress(), partialAmount); + const strangerBalance = await link.balanceOf(await roles.stranger.getAddress()); + const oracleBalance = await link.balanceOf(operator.address); + assert.equal(partialAmount, strangerBalance.toNumber()); + assert.equal(difference, oracleBalance.toNumber()); + }); + + it("allows transfer of entire balance by owner to specified address", async () => { + await operator.connect(roles.defaultAccount).withdraw(await roles.stranger.getAddress(), payment); + const balance = await link.balanceOf(await roles.stranger.getAddress()); + assert.equal(payment, balance.toNumber()); + }); + + it("does not allow a transfer of funds by non-owner", async () => { + await evmRevert(operator.connect(roles.stranger).withdraw(await roles.stranger.getAddress(), payment)); + const balance = await link.balanceOf(await roles.stranger.getAddress()); + assert.isTrue(ethers.constants.Zero.eq(balance)); + }); + + describe("recovering funds that were mistakenly sent", () => { + const paid = 1; + beforeEach(async () => { + await link.transfer(operator.address, paid); + }); + + it("withdraws funds", async () => { + const operatorBalanceBefore = await link.balanceOf(operator.address); + const accountBalanceBefore = await link.balanceOf(await roles.defaultAccount.getAddress()); + + await operator.connect(roles.defaultAccount).withdraw(await roles.defaultAccount.getAddress(), paid); + + const operatorBalanceAfter = await link.balanceOf(operator.address); + const accountBalanceAfter = await link.balanceOf(await roles.defaultAccount.getAddress()); + + const accountDifference = accountBalanceAfter.sub(accountBalanceBefore); + const operatorDifference = operatorBalanceBefore.sub(operatorBalanceAfter); + + bigNumEquals(operatorDifference, paid); + bigNumEquals(accountDifference, paid); + }); + }); + }); + }); + }); + + describe("#withdrawable", () => { + let request: ReturnType; + const amount = toWei("1"); + + beforeEach(async () => { + const mock = await getterSetterFactory.connect(roles.defaultAccount).deploy(); + const args = encodeOracleRequest(specId, mock.address, fHash, 0, constants.HashZero); + const tx = await link.transferAndCall(operator.address, amount, args); + const receipt = await tx.wait(); + assert.equal(3, receipt.logs?.length); + request = decodeRunRequest(receipt.logs?.[2]); + await operator.connect(roles.oracleNode).fulfillOracleRequest(...convertFufillParams(request, "Hello World!")); + }); + + it("returns the correct value", async () => { + const withdrawAmount = await operator.withdrawable(); + bigNumEquals(withdrawAmount, request.payment); + }); + + describe("funds that were mistakenly sent", () => { + const paid = 1; + beforeEach(async () => { + await link.transfer(operator.address, paid); + }); + + it("returns the correct value", async () => { + const withdrawAmount = await operator.withdrawable(); + + const expectedAmount = amount.add(paid); + bigNumEquals(withdrawAmount, expectedAmount); + }); + }); + }); + + describe("#ownerTransferAndCall", () => { + let operator2: Contract; + let args: string; + let to: string; + const startingBalance = 1000; + const payment = 20; + + beforeEach(async () => { + operator2 = await operatorFactory + .connect(roles.oracleNode2) + .deploy(link.address, await roles.oracleNode2.getAddress()); + to = operator2.address; + args = encodeOracleRequest( + specId, + operator.address, + operatorFactory.interface.getSighash("fulfillOracleRequest"), + 1, + constants.HashZero, + ); + }); + + describe("when called by a non-owner", () => { + it("reverts with owner error message", async () => { + await link.transfer(operator.address, startingBalance); + await evmRevert( + operator.connect(roles.stranger).ownerTransferAndCall(to, payment, args), + "Only callable by owner", + ); + }); + }); + + describe("when called by the owner", () => { + beforeEach(async () => { + await link.transfer(operator.address, startingBalance); + }); + + describe("without sufficient funds in contract", () => { + it("reverts with funds message", async () => { + const tooMuch = startingBalance * 2; + await evmRevert( + operator.connect(roles.defaultAccount).ownerTransferAndCall(to, tooMuch, args), + "Amount requested is greater than withdrawable balance", + ); + }); + }); + + describe("with sufficient funds", () => { + let tx: ContractTransaction; + let receipt: ContractReceipt; + let requesterBalanceBefore: BigNumber; + let requesterBalanceAfter: BigNumber; + let receiverBalanceBefore: BigNumber; + let receiverBalanceAfter: BigNumber; + + before(async () => { + requesterBalanceBefore = await link.balanceOf(operator.address); + receiverBalanceBefore = await link.balanceOf(operator2.address); + tx = await operator.connect(roles.defaultAccount).ownerTransferAndCall(to, payment, args); + receipt = await tx.wait(); + requesterBalanceAfter = await link.balanceOf(operator.address); + receiverBalanceAfter = await link.balanceOf(operator2.address); + }); + + it("emits an event", async () => { + assert.equal(3, receipt.logs?.length); + expect(tx).to.emit(link, "Transfer").withArgs(operator.address, to, payment, args); + }); + + it("transfers the tokens", async () => { + bigNumEquals(requesterBalanceBefore.sub(requesterBalanceAfter), payment); + bigNumEquals(receiverBalanceAfter.sub(receiverBalanceBefore), payment); + }); + }); + }); + }); + + describe("#cancelOracleRequest", () => { + describe("with no pending requests", () => { + it("fails", async () => { + const fakeRequest: RunRequest = { + requestId: ethers.utils.formatBytes32String("1337"), + payment: "0", + callbackFunc: getterSetterFactory.interface.getSighash("requestedBytes32"), + expiration: "999999999999", + + callbackAddr: "", + data: Buffer.from(""), + dataVersion: 0, + specId: "", + requester: "", + topic: "", + }; + await increaseTime5Minutes(ethers.provider); + + await evmRevert(operator.connect(roles.stranger).cancelOracleRequest(...convertCancelParams(fakeRequest))); + }); + }); + + describe("with a pending request", () => { + const startingBalance = 100; + let request: ReturnType; + let receipt: providers.TransactionReceipt; + + beforeEach(async () => { + const requestAmount = 20; + + await link.transfer(await roles.consumer.getAddress(), startingBalance); + + const args = encodeOracleRequest(specId, await roles.consumer.getAddress(), fHash, 1, constants.HashZero); + const tx = await link.connect(roles.consumer).transferAndCall(operator.address, requestAmount, args); + receipt = await tx.wait(); + + assert.equal(3, receipt.logs?.length); + request = decodeRunRequest(receipt.logs?.[2]); + }); + + it("has correct initial balances", async () => { + const oracleBalance = await link.balanceOf(operator.address); + bigNumEquals(request.payment, oracleBalance); + + const consumerAmount = await link.balanceOf(await roles.consumer.getAddress()); + assert.equal(startingBalance - Number(request.payment), consumerAmount.toNumber()); + }); + + describe("from a stranger", () => { + it("fails", async () => { + await evmRevert(operator.connect(roles.consumer).cancelOracleRequest(...convertCancelParams(request))); + }); + }); + + describe("from the requester", () => { + it("refunds the correct amount", async () => { + await increaseTime5Minutes(ethers.provider); + await operator.connect(roles.consumer).cancelOracleRequest(...convertCancelParams(request)); + const balance = await link.balanceOf(await roles.consumer.getAddress()); + + assert.equal(startingBalance, balance.toNumber()); // 100 + }); + + it("triggers a cancellation event", async () => { + await increaseTime5Minutes(ethers.provider); + const tx = await operator.connect(roles.consumer).cancelOracleRequest(...convertCancelParams(request)); + const receipt = await tx.wait(); + + assert.equal(receipt.logs?.length, 2); + assert.equal(request.requestId, receipt.logs?.[0].topics[1]); + }); + + it("fails when called twice", async () => { + await increaseTime5Minutes(ethers.provider); + await operator.connect(roles.consumer).cancelOracleRequest(...convertCancelParams(request)); + + await evmRevert(operator.connect(roles.consumer).cancelOracleRequest(...convertCancelParams(request))); + }); + }); + }); + }); + + describe("#ownerForward", () => { + let bytes: string; + let payload: string; + let mock: Contract; + + beforeEach(async () => { + bytes = ethers.utils.hexlify(ethers.utils.randomBytes(100)); + payload = getterSetterFactory.interface.encodeFunctionData( + getterSetterFactory.interface.getFunction("setBytes"), + [bytes], + ); + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy(); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert(operator.connect(roles.stranger).ownerForward(mock.address, payload)); + }); + }); + + describe("when called by owner", () => { + describe("when attempting to forward to the link token", () => { + it("reverts", async () => { + const sighash = linkTokenFactory.interface.getSighash("name"); + await evmRevert( + operator.connect(roles.defaultAccount).ownerForward(link.address, sighash), + "Cannot call to LINK", + ); + }); + }); + + describe("when forwarding to any other address", () => { + it("forwards the data", async () => { + const tx = await operator.connect(roles.defaultAccount).ownerForward(mock.address, payload); + await tx.wait(); + assert.equal(await mock.getBytes(), bytes); + }); + + it("reverts when sending to a non-contract address", async () => { + await evmRevert( + operator.connect(roles.defaultAccount).ownerForward(zeroAddress, payload), + "Must forward to a contract", + ); + }); + + it("perceives the message is sent by the Operator", async () => { + const tx = await operator.connect(roles.defaultAccount).ownerForward(mock.address, payload); + const receipt = await tx.wait(); + const log: any = receipt.logs?.[0]; + const logData = mock.interface.decodeEventLog(mock.interface.getEvent("SetBytes"), log.data, log.topics); + assert.equal(ethers.utils.getAddress(logData.from), operator.address); + }); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/OperatorFactory.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/OperatorFactory.test.ts new file mode 100644 index 0000000..44bea52 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/OperatorFactory.test.ts @@ -0,0 +1,187 @@ +import { ethers } from "hardhat"; +import { evmWordToAddress, publicAbi } from "../test-helpers/helpers"; +import { assert } from "chai"; +import { Contract, ContractFactory, ContractReceipt } from "ethers"; +import { getUsers, Roles } from "../test-helpers/setup"; + +let linkTokenFactory: ContractFactory; +let operatorGeneratorFactory: ContractFactory; +let operatorFactory: ContractFactory; +let forwarderFactory: ContractFactory; + +let roles: Roles; + +before(async () => { + const users = await getUsers(); + + roles = users.roles; + linkTokenFactory = await ethers.getContractFactory("LinkToken", roles.defaultAccount); + operatorGeneratorFactory = await ethers.getContractFactory("OperatorFactory", roles.defaultAccount); + operatorFactory = await ethers.getContractFactory("Operator", roles.defaultAccount); + forwarderFactory = await ethers.getContractFactory("AuthorizedForwarder", roles.defaultAccount); +}); + +describe("OperatorFactory", () => { + let link: Contract; + let operatorGenerator: Contract; + let operator: Contract; + let forwarder: Contract; + let receipt: ContractReceipt; + let emittedOperator: string; + let emittedForwarder: string; + + beforeEach(async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy(); + operatorGenerator = await operatorGeneratorFactory.connect(roles.defaultAccount).deploy(link.address); + }); + + it("has a limited public interface", () => { + publicAbi(operatorGenerator, [ + "created", + "deployNewOperator", + "deployNewOperatorAndForwarder", + "deployNewForwarder", + "deployNewForwarderAndTransferOwnership", + "getChainlinkToken", + ]); + }); + + describe("#deployNewOperator", () => { + beforeEach(async () => { + const tx = await operatorGenerator.connect(roles.oracleNode).deployNewOperator(); + + receipt = await tx.wait(); + emittedOperator = evmWordToAddress(receipt.logs?.[0].topics?.[1]); + }); + + it("emits an event", async () => { + assert.equal(receipt?.events?.[0]?.event, "OperatorCreated"); + assert.equal(emittedOperator, receipt.events?.[0].args?.[0]); + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[0].args?.[1]); + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[0].args?.[2]); + }); + + it("sets the correct owner", async () => { + operator = await operatorFactory.connect(roles.defaultAccount).attach(emittedOperator); + const ownerString = await operator.owner(); + assert.equal(ownerString, await roles.oracleNode.getAddress()); + }); + + it("records that it deployed that address", async () => { + assert.isTrue(await operatorGenerator.created(emittedOperator)); + }); + }); + + describe("#deployNewOperatorAndForwarder", () => { + beforeEach(async () => { + const tx = await operatorGenerator.connect(roles.oracleNode).deployNewOperatorAndForwarder(); + + receipt = await tx.wait(); + emittedOperator = evmWordToAddress(receipt.logs?.[0].topics?.[1]); + emittedForwarder = evmWordToAddress(receipt.logs?.[1].topics?.[1]); + }); + + it("emits an event recording that the operator was deployed", async () => { + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[0].args?.[1]); + assert.equal(receipt?.events?.[0]?.event, "OperatorCreated"); + assert.equal(receipt?.events?.[0]?.args?.[0], emittedOperator); + assert.equal(receipt?.events?.[0]?.args?.[1], await roles.oracleNode.getAddress()); + assert.equal(receipt?.events?.[0]?.args?.[2], await roles.oracleNode.getAddress()); + }); + + it("emits an event recording that the forwarder was deployed", async () => { + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[0].args?.[1]); + assert.equal(receipt?.events?.[1]?.event, "AuthorizedForwarderCreated"); + assert.equal(receipt?.events?.[1]?.args?.[0], emittedForwarder); + assert.equal(receipt?.events?.[1]?.args?.[1], emittedOperator); + assert.equal(receipt?.events?.[1]?.args?.[2], await roles.oracleNode.getAddress()); + }); + + it("sets the correct owner on the operator", async () => { + operator = await operatorFactory.connect(roles.defaultAccount).attach(receipt?.events?.[0]?.args?.[0]); + assert.equal(await roles.oracleNode.getAddress(), await operator.owner()); + }); + + it("sets the operator as the owner of the forwarder", async () => { + forwarder = await forwarderFactory.connect(roles.defaultAccount).attach(receipt?.events?.[1]?.args?.[0]); + const operatorAddress = receipt?.events?.[0]?.args?.[0]; + assert.equal(operatorAddress, await forwarder.owner()); + }); + + it("records that it deployed that address", async () => { + assert.isTrue(await operatorGenerator.created(emittedOperator)); + assert.isTrue(await operatorGenerator.created(emittedForwarder)); + }); + }); + + describe("#deployNewForwarder", () => { + beforeEach(async () => { + const tx = await operatorGenerator.connect(roles.oracleNode).deployNewForwarder(); + + receipt = await tx.wait(); + emittedForwarder = receipt.events?.[0].args?.[0]; + }); + + it("emits an event", async () => { + assert.equal(receipt?.events?.[0]?.event, "AuthorizedForwarderCreated"); + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[0].args?.[1]); // owner + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[0].args?.[2]); // sender + }); + + it("sets the caller as the owner", async () => { + forwarder = await forwarderFactory.connect(roles.defaultAccount).attach(emittedForwarder); + const ownerString = await forwarder.owner(); + assert.equal(ownerString, await roles.oracleNode.getAddress()); + }); + + it("records that it deployed that address", async () => { + assert.isTrue(await operatorGenerator.created(emittedForwarder)); + }); + }); + + describe("#deployNewForwarderAndTransferOwnership", () => { + const message = "0x42"; + + beforeEach(async () => { + const tx = await operatorGenerator + .connect(roles.oracleNode) + .deployNewForwarderAndTransferOwnership(await roles.stranger.getAddress(), message); + receipt = await tx.wait(); + + emittedForwarder = evmWordToAddress(receipt.logs?.[2].topics?.[1]); + }); + + it("emits an event", async () => { + assert.equal(receipt?.events?.[2]?.event, "AuthorizedForwarderCreated"); + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[2].args?.[1]); // owner + assert.equal(await roles.oracleNode.getAddress(), receipt.events?.[2].args?.[2]); // sender + }); + + it("sets the caller as the owner", async () => { + forwarder = await forwarderFactory.connect(roles.defaultAccount).attach(emittedForwarder); + const ownerString = await forwarder.owner(); + assert.equal(ownerString, await roles.oracleNode.getAddress()); + }); + + it("proposes a transfer to the recipient", async () => { + const emittedOwner = evmWordToAddress(receipt.logs?.[0].topics?.[1]); + assert.equal(emittedOwner, await roles.oracleNode.getAddress()); + const emittedRecipient = evmWordToAddress(receipt.logs?.[0].topics?.[2]); + assert.equal(emittedRecipient, await roles.stranger.getAddress()); + }); + + it("proposes a transfer to the recipient with the specified message", async () => { + const emittedOwner = evmWordToAddress(receipt.logs?.[1].topics?.[1]); + assert.equal(emittedOwner, await roles.oracleNode.getAddress()); + const emittedRecipient = evmWordToAddress(receipt.logs?.[1].topics?.[2]); + assert.equal(emittedRecipient, await roles.stranger.getAddress()); + + const encodedMessage = ethers.utils.defaultAbiCoder.encode(["bytes"], [message]); + assert.equal(receipt?.logs?.[1]?.data, encodedMessage); + }); + + it("records that it deployed that address", async () => { + assert.isTrue(await operatorGenerator.created(emittedForwarder)); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/OperatorForwarder.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/OperatorForwarder.test.ts new file mode 100644 index 0000000..b989668 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/OperatorForwarder.test.ts @@ -0,0 +1,140 @@ +import { + contract, + helpers as h, + matchers, + // oracle, + setup, +} from '@chainlink/test-helpers' +import { assert } from 'chai' +import { utils } from 'ethers' +import { GetterSetter__factory } from '../../ethers/v0.4/factories/GetterSetter__factory' +import { OperatorForwarder__factory } from '../../ethers/v0.7/factories/OperatorForwarder__factory' +import { OperatorForwarderDeployer__factory } from '../../ethers/v0.7/factories/OperatorForwarderDeployer__factory' + +const getterSetterFactory = new GetterSetter__factory() +const operatorForwarderFactory = new OperatorForwarder__factory() +const operatorForwarderDeployerFactory = new OperatorForwarderDeployer__factory() +const linkTokenFactory = new contract.LinkToken__factory() + +let roles: setup.Roles +const provider = setup.provider() + +beforeAll(async () => { + const users = await setup.users(provider) + + roles = users.roles +}) + +describe('OperatorForwarder', () => { + let authorizedSenders: string[] + let link: contract.Instance + let operatorForwarderDeployer: contract.Instance + let operatorForwarder: contract.Instance + const deployment = setup.snapshot(provider, async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy() + authorizedSenders = [roles.oracleNode2.address, roles.oracleNode3.address] + operatorForwarderDeployer = await operatorForwarderDeployerFactory + .connect(roles.defaultAccount) + .deploy(link.address, authorizedSenders) + const tx = await operatorForwarderDeployer.createForwarder() + const receipt = await tx.wait() + const event = h.findEventIn( + receipt, + operatorForwarderDeployer.interface.events.ForwarderDeployed, + ) + operatorForwarder = await operatorForwarderFactory + .connect(roles.defaultAccount) + .attach(event?.args?.[0]) + }) + + beforeEach(async () => { + await deployment() + }) + + it('has a limited public interface', () => { + matchers.publicAbi(operatorForwarder, [ + 'authorizedSender1', + 'authorizedSender2', + 'authorizedSender3', + 'linkAddr', + 'forward', + ]) + }) + + describe('deployment', () => { + it('sets the correct link token', async () => { + const forwarderLink = await operatorForwarder.linkAddr() + assert.equal(forwarderLink, link.address) + }) + + it('sets the correct authorized senders', async () => { + const auth1 = await operatorForwarder.authorizedSender1() + const auth2 = await operatorForwarder.authorizedSender2() + const auth3 = await operatorForwarder.authorizedSender3() + assert.equal(auth1, roles.defaultAccount.address) + assert.equal(auth2, authorizedSenders[0]) + assert.equal(auth3, authorizedSenders[1]) + }) + }) + + describe('#forward', () => { + const bytes = utils.hexlify(utils.randomBytes(100)) + const payload = getterSetterFactory.interface.functions.setBytes.encode([ + bytes, + ]) + let mock: contract.Instance + + beforeEach(async () => { + mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() + }) + + describe('when called by an unauthorized node', () => { + it('reverts', async () => { + await matchers.evmRevert(async () => { + await operatorForwarder + .connect(roles.stranger) + .forward(mock.address, payload) + }) + }) + }) + + describe('when called by an authorized node', () => { + describe('when attempting to forward to the link token', () => { + it('reverts', async () => { + const { sighash } = linkTokenFactory.interface.functions.name // any Link Token function + await matchers.evmRevert(async () => { + await operatorForwarder + .connect(roles.defaultAccount) + .forward(link.address, sighash) + }) + }) + }) + + describe('when forwarding to any other address', () => { + it('forwards the data', async () => { + const tx = await operatorForwarder + .connect(roles.defaultAccount) + .forward(mock.address, payload) + await tx.wait() + assert.equal(await mock.getBytes(), bytes) + }) + + it('perceives the message is sent by the OperatorForwarder', async () => { + const tx = await operatorForwarder + .connect(roles.defaultAccount) + .forward(mock.address, payload) + const receipt = await tx.wait() + const log: any = receipt.logs?.[0] + const logData = mock.interface.events.SetBytes.decode( + log.data, + log.topics, + ) + assert.equal( + utils.getAddress(logData.from), + operatorForwarder.address, + ) + }) + }) + }) + }) +}) diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/StalenessFlaggingValidator.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/StalenessFlaggingValidator.test.ts new file mode 100644 index 0000000..81f8f84 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/StalenessFlaggingValidator.test.ts @@ -0,0 +1,445 @@ +import { ethers } from "hardhat"; +import { evmWordToAddress, getLog, getLogs, numToBytes32, publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { BigNumber, Contract, ContractFactory } from "ethers"; +import { Personas, getUsers } from "../test-helpers/setup"; +import { evmRevert } from "../test-helpers/matchers"; + +let personas: Personas; +let validatorFactory: ContractFactory; +let flagsFactory: ContractFactory; +let acFactory: ContractFactory; +let aggregatorFactory: ContractFactory; + +before(async () => { + personas = (await getUsers()).personas; + + validatorFactory = await ethers.getContractFactory("StalenessFlaggingValidator", personas.Carol); + flagsFactory = await ethers.getContractFactory("Flags", personas.Carol); + acFactory = await ethers.getContractFactory("SimpleWriteAccessController", personas.Carol); + aggregatorFactory = await ethers.getContractFactory("MockV3Aggregator", personas.Carol); +}); + +describe("StalenessFlaggingValidator", () => { + let validator: Contract; + let flags: Contract; + let ac: Contract; + + const flaggingThreshold1 = 10000; + const flaggingThreshold2 = 20000; + + beforeEach(async () => { + ac = await acFactory.connect(personas.Carol).deploy(); + flags = await flagsFactory.connect(personas.Carol).deploy(ac.address); + validator = await validatorFactory.connect(personas.Carol).deploy(flags.address); + + await ac.connect(personas.Carol).addAccess(validator.address); + }); + + it("has a limited public interface", () => { + publicAbi(validator, [ + "update", + "check", + "setThresholds", + "setFlagsAddress", + "threshold", + "flags", + // Upkeep methods: + "checkUpkeep", + "performUpkeep", + // Owned methods: + "acceptOwnership", + "owner", + "transferOwnership", + ]); + }); + + describe("#constructor", () => { + it("sets the arguments passed in", async () => { + assert.equal(await validator.flags(), flags.address); + }); + + it("sets the owner", async () => { + assert.equal(await validator.owner(), await personas.Carol.getAddress()); + }); + }); + + describe("#setFlagsAddress", () => { + const newFlagsAddress = "0x0123456789012345678901234567890123456789"; + + it("changes the flags address", async () => { + assert.equal(flags.address, await validator.flags()); + + await validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress); + + assert.equal(newFlagsAddress, await validator.flags()); + }); + + it("emits a log event only when actually changed", async () => { + const tx = await validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress); + await expect(tx).to.emit(validator, "FlagsAddressUpdated").withArgs(flags.address, newFlagsAddress); + + const sameChangeTx = await validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress); + + await expect(sameChangeTx).to.not.emit(validator, "FlagsAddressUpdated"); + }); + + describe("when called by a non-owner", () => { + it("reverts", async () => { + await evmRevert(validator.connect(personas.Neil).setFlagsAddress(newFlagsAddress), "Only callable by owner"); + }); + }); + }); + + describe("#setThresholds", () => { + let agg1: Contract; + let agg2: Contract; + let aggregators: Array; + let thresholds: Array; + + beforeEach(async () => { + const decimals = 8; + const initialAnswer = 10000000000; + agg1 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + agg2 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + }); + + describe("failure", () => { + beforeEach(() => { + aggregators = [agg1.address, agg2.address]; + thresholds = [flaggingThreshold1]; + }); + + it("reverts when called by a non-owner", async () => { + await evmRevert( + validator.connect(personas.Neil).setThresholds(aggregators, thresholds), + "Only callable by owner", + ); + }); + + it("reverts when passed uneven arrays", async () => { + await evmRevert( + validator.connect(personas.Carol).setThresholds(aggregators, thresholds), + "Different sized arrays", + ); + }); + }); + + describe("success", () => { + let tx: any; + + beforeEach(() => { + aggregators = [agg1.address, agg2.address]; + thresholds = [flaggingThreshold1, flaggingThreshold2]; + }); + + describe("when called with 2 new thresholds", () => { + beforeEach(async () => { + tx = await validator.connect(personas.Carol).setThresholds(aggregators, thresholds); + }); + + it("sets the thresholds", async () => { + const first = await validator.threshold(agg1.address); + const second = await validator.threshold(agg2.address); + assert.equal(first.toString(), flaggingThreshold1.toString()); + assert.equal(second.toString(), flaggingThreshold2.toString()); + }); + + it("emits events", async () => { + const firstEvent = await getLog(tx, 0); + assert.equal(evmWordToAddress(firstEvent.topics[1]), agg1.address); + assert.equal(firstEvent.topics[3], numToBytes32(flaggingThreshold1)); + const secondEvent = await getLog(tx, 1); + assert.equal(evmWordToAddress(secondEvent.topics[1]), agg2.address); + assert.equal(secondEvent.topics[3], numToBytes32(flaggingThreshold2)); + }); + }); + + describe("when called with 2, but 1 has not changed", () => { + it("emits only 1 event", async () => { + tx = await validator.connect(personas.Carol).setThresholds(aggregators, thresholds); + + const newThreshold = flaggingThreshold2 + 1; + tx = await validator.connect(personas.Carol).setThresholds(aggregators, [flaggingThreshold1, newThreshold]); + const logs = await getLogs(tx); + assert.equal(logs.length, 1); + const log = logs[0]; + assert.equal(evmWordToAddress(log.topics[1]), agg2.address); + assert.equal(log.topics[2], numToBytes32(flaggingThreshold2)); + assert.equal(log.topics[3], numToBytes32(newThreshold)); + }); + }); + }); + }); + + describe("#check", () => { + let agg1: Contract; + let agg2: Contract; + let aggregators: Array; + let thresholds: Array; + const decimals = 8; + const initialAnswer = 10000000000; + beforeEach(async () => { + agg1 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + agg2 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + aggregators = [agg1.address, agg2.address]; + thresholds = [flaggingThreshold1, flaggingThreshold2]; + await validator.setThresholds(aggregators, thresholds); + }); + + describe("when neither are stale", () => { + it("returns an empty array", async () => { + const response = await validator.check(aggregators); + assert.equal(response.length, 0); + }); + }); + + describe("when threshold is not set in the validator", () => { + it("returns an empty array", async () => { + const agg3 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + const response = await validator.check([agg3.address]); + assert.equal(response.length, 0); + }); + }); + + describe("when one of the aggregators is stale", () => { + it("returns an array with one stale aggregator", async () => { + const currentTimestamp = await agg1.latestTimestamp(); + const staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + const response = await validator.check(aggregators); + + assert.equal(response.length, 1); + assert.equal(response[0], agg1.address); + }); + }); + + describe("When both aggregators are stale", () => { + it("returns an array with both aggregators", async () => { + let currentTimestamp = await agg1.latestTimestamp(); + let staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + currentTimestamp = await agg2.latestTimestamp(); + staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold2 + 1)); + await agg2.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const response = await validator.check(aggregators); + + assert.equal(response.length, 2); + assert.equal(response[0], agg1.address); + assert.equal(response[1], agg2.address); + }); + }); + }); + + describe("#update", () => { + let agg1: Contract; + let agg2: Contract; + let aggregators: Array; + let thresholds: Array; + const decimals = 8; + const initialAnswer = 10000000000; + beforeEach(async () => { + agg1 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + agg2 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + aggregators = [agg1.address, agg2.address]; + thresholds = [flaggingThreshold1, flaggingThreshold2]; + await validator.setThresholds(aggregators, thresholds); + }); + + describe("when neither are stale", () => { + it("does not raise a flag", async () => { + const tx = await validator.update(aggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + + describe("when threshold is not set in the validator", () => { + it("does not raise a flag", async () => { + const agg3 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + const tx = await validator.update([agg3.address]); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + + describe("when one is stale", () => { + it("raises a flag for that aggregator", async () => { + const currentTimestamp = await agg1.latestTimestamp(); + const staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const tx = await validator.update(aggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 1); + assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address); + }); + }); + + describe("when both are stale", () => { + it("raises 2 flags, one for each aggregator", async () => { + let currentTimestamp = await agg1.latestTimestamp(); + let staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + currentTimestamp = await agg2.latestTimestamp(); + staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold2 + 1)); + await agg2.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const tx = await validator.update(aggregators); + const logs = await getLogs(tx); + assert.equal(logs.length, 2); + assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address); + assert.equal(evmWordToAddress(logs[1].topics[1]), agg2.address); + }); + }); + }); + + describe("#checkUpkeep", () => { + let agg1: Contract; + let agg2: Contract; + let aggregators: Array; + let thresholds: Array; + const decimals = 8; + const initialAnswer = 10000000000; + beforeEach(async () => { + agg1 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + agg2 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + aggregators = [agg1.address, agg2.address]; + thresholds = [flaggingThreshold1, flaggingThreshold2]; + await validator.setThresholds(aggregators, thresholds); + }); + + describe("when neither are stale", () => { + it("returns false and an empty array", async () => { + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const response = await validator.checkUpkeep(bytesData); + + assert.equal(response[0], false); + const decodedResponse = ethers.utils.defaultAbiCoder.decode(["address[]"], response?.[1]); + assert.equal(decodedResponse[0].length, 0); + }); + }); + + describe("when threshold is not set in the validator", () => { + it("returns flase and an empty array", async () => { + const agg3 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [[agg3.address]]); + const response = await validator.checkUpkeep(bytesData); + + assert.equal(response[0], false); + const decodedResponse = ethers.utils.defaultAbiCoder.decode(["address[]"], response?.[1]); + assert.equal(decodedResponse[0].length, 0); + }); + }); + + describe("when one of the aggregators is stale", () => { + it("returns true with an array with one stale aggregator", async () => { + const currentTimestamp = await agg1.latestTimestamp(); + const staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const response = await validator.checkUpkeep(bytesData); + + assert.equal(response[0], true); + const decodedResponse = ethers.utils.defaultAbiCoder.decode(["address[]"], response?.[1]); + const decodedArray = decodedResponse[0]; + assert.equal(decodedArray.length, 1); + assert.equal(decodedArray[0], agg1.address); + }); + }); + + describe("When both aggregators are stale", () => { + it("returns true with an array with both aggregators", async () => { + let currentTimestamp = await agg1.latestTimestamp(); + let staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + currentTimestamp = await agg2.latestTimestamp(); + staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold2 + 1)); + await agg2.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const response = await validator.checkUpkeep(bytesData); + + assert.equal(response[0], true); + const decodedResponse = ethers.utils.defaultAbiCoder.decode(["address[]"], response?.[1]); + const decodedArray = decodedResponse[0]; + assert.equal(decodedArray.length, 2); + assert.equal(decodedArray[0], agg1.address); + assert.equal(decodedArray[1], agg2.address); + }); + }); + }); + + describe("#performUpkeep", () => { + let agg1: Contract; + let agg2: Contract; + let aggregators: Array; + let thresholds: Array; + const decimals = 8; + const initialAnswer = 10000000000; + beforeEach(async () => { + agg1 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + agg2 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + aggregators = [agg1.address, agg2.address]; + thresholds = [flaggingThreshold1, flaggingThreshold2]; + await validator.setThresholds(aggregators, thresholds); + }); + + describe("when neither are stale", () => { + it("does not raise a flag", async () => { + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const tx = await validator.performUpkeep(bytesData); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + + describe("when threshold is not set in the validator", () => { + it("does not raise a flag", async () => { + const agg3 = await aggregatorFactory.connect(personas.Carol).deploy(decimals, initialAnswer); + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [[agg3.address]]); + const tx = await validator.performUpkeep(bytesData); + const logs = await getLogs(tx); + assert.equal(logs.length, 0); + }); + }); + + describe("when one is stale", () => { + it("raises a flag for that aggregator", async () => { + const currentTimestamp = await agg1.latestTimestamp(); + const staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const tx = await validator.performUpkeep(bytesData); + const logs = await getLogs(tx); + assert.equal(logs.length, 1); + assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address); + }); + }); + + describe("when both are stale", () => { + it("raises 2 flags, one for each aggregator", async () => { + let currentTimestamp = await agg1.latestTimestamp(); + let staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold1 + 1)); + await agg1.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + currentTimestamp = await agg2.latestTimestamp(); + staleTimestamp = currentTimestamp.sub(BigNumber.from(flaggingThreshold2 + 1)); + await agg2.updateRoundData(99, initialAnswer, staleTimestamp, staleTimestamp); + + const bytesData = ethers.utils.defaultAbiCoder.encode(["address[]"], [aggregators]); + const tx = await validator.performUpkeep(bytesData); + const logs = await getLogs(tx); + assert.equal(logs.length, 2); + assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address); + assert.equal(evmWordToAddress(logs[1].topics[1]), agg2.address); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.7/gasUsage.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.7/gasUsage.test.ts new file mode 100644 index 0000000..0683a9c --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.7/gasUsage.test.ts @@ -0,0 +1,143 @@ +import { ethers } from "hardhat"; +import { toBytes32String, toWei } from "../test-helpers/helpers"; +import { Contract, ContractFactory } from "ethers"; +import { getUsers, Roles } from "../test-helpers/setup"; +import { convertFufillParams, convertFulfill2Params, decodeRunRequest } from "../test-helpers/oracle"; +import { gasDiffLessThan } from "../test-helpers/matchers"; + +let operatorFactory: ContractFactory; +let oracleFactory: ContractFactory; +let basicConsumerFactory: ContractFactory; +let linkTokenFactory: ContractFactory; + +let roles: Roles; + +before(async () => { + const users = await getUsers(); + + roles = users.roles; + operatorFactory = await ethers.getContractFactory("Operator", roles.defaultAccount); + oracleFactory = await ethers.getContractFactory("src/v0.6/Oracle.sol:Oracle", roles.defaultAccount); + basicConsumerFactory = await ethers.getContractFactory( + "src/v0.6/tests/BasicConsumer.sol:BasicConsumer", + roles.defaultAccount, + ); + linkTokenFactory = await ethers.getContractFactory("LinkToken", roles.defaultAccount); +}); + +describe("Operator Gas Tests", () => { + const specId = "0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000"; + let link: Contract; + let oracle1: Contract; + let operator1: Contract; + let operator2: Contract; + + beforeEach(async () => { + link = await linkTokenFactory.connect(roles.defaultAccount).deploy(); + + operator1 = await operatorFactory + .connect(roles.defaultAccount) + .deploy(link.address, await roles.defaultAccount.getAddress()); + await operator1.setAuthorizedSenders([await roles.oracleNode.getAddress()]); + + operator2 = await operatorFactory + .connect(roles.defaultAccount) + .deploy(link.address, await roles.defaultAccount.getAddress()); + await operator2.setAuthorizedSenders([await roles.oracleNode.getAddress()]); + + oracle1 = await oracleFactory.connect(roles.defaultAccount).deploy(link.address); + await oracle1.setFulfillmentPermission(await roles.oracleNode.getAddress(), true); + }); + + // Test Oracle.fulfillOracleRequest vs Operator.fulfillOracleRequest + describe("v0.6/Oracle vs v0.7/Operator #fulfillOracleRequest", () => { + const response = "Hi Mom!"; + let basicConsumer1: Contract; + let basicConsumer2: Contract; + + let request1: ReturnType; + let request2: ReturnType; + + beforeEach(async () => { + basicConsumer1 = await basicConsumerFactory.connect(roles.consumer).deploy(link.address, oracle1.address, specId); + basicConsumer2 = await basicConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator1.address, specId); + + const paymentAmount = toWei("1"); + const currency = "USD"; + + await link.transfer(basicConsumer1.address, paymentAmount); + const tx1 = await basicConsumer1.requestEthereumPrice(currency, paymentAmount); + const receipt1 = await tx1.wait(); + request1 = decodeRunRequest(receipt1.logs?.[3]); + + await link.transfer(basicConsumer2.address, paymentAmount); + const tx2 = await basicConsumer2.requestEthereumPrice(currency, paymentAmount); + const receipt2 = await tx2.wait(); + request2 = decodeRunRequest(receipt2.logs?.[3]); + }); + + it("uses acceptable gas", async () => { + const tx1 = await oracle1 + .connect(roles.oracleNode) + .fulfillOracleRequest(...convertFufillParams(request1, response)); + const tx2 = await operator1 + .connect(roles.oracleNode) + .fulfillOracleRequest(...convertFufillParams(request2, response)); + const receipt1 = await tx1.wait(); + const receipt2 = await tx2.wait(); + // 38014 vs 40260 + gasDiffLessThan(2500, receipt1, receipt2); + }); + }); + + // Test Operator1.fulfillOracleRequest vs Operator2.fulfillOracleRequest2 + // with single word response + describe("Operator #fulfillOracleRequest vs #fulfillOracleRequest2", () => { + const response = "Hi Mom!"; + let basicConsumer1: Contract; + let basicConsumer2: Contract; + + let request1: ReturnType; + let request2: ReturnType; + + beforeEach(async () => { + basicConsumer1 = await basicConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator1.address, specId); + basicConsumer2 = await basicConsumerFactory + .connect(roles.consumer) + .deploy(link.address, operator2.address, specId); + + const paymentAmount = toWei("1"); + const currency = "USD"; + + await link.transfer(basicConsumer1.address, paymentAmount); + const tx1 = await basicConsumer1.requestEthereumPrice(currency, paymentAmount); + const receipt1 = await tx1.wait(); + request1 = decodeRunRequest(receipt1.logs?.[3]); + + await link.transfer(basicConsumer2.address, paymentAmount); + const tx2 = await basicConsumer2.requestEthereumPrice(currency, paymentAmount); + const receipt2 = await tx2.wait(); + request2 = decodeRunRequest(receipt2.logs?.[3]); + }); + + it("uses acceptable gas", async () => { + const tx1 = await operator1 + .connect(roles.oracleNode) + .fulfillOracleRequest(...convertFufillParams(request1, response)); + + const responseTypes = ["bytes32"]; + const responseValues = [toBytes32String(response)]; + const tx2 = await operator2 + .connect(roles.oracleNode) + .fulfillOracleRequest2(...convertFulfill2Params(request2, responseTypes, responseValues)); + + const receipt1 = await tx1.wait(); + const receipt2 = await tx2.wait(); + gasDiffLessThan(1240, receipt1, receipt2); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/test/v0.8/ValidatorProxy.test.ts b/lib/chainlink-brownie-contracts/contracts/test/v0.8/ValidatorProxy.test.ts new file mode 100644 index 0000000..0f8265f --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/test/v0.8/ValidatorProxy.test.ts @@ -0,0 +1,344 @@ +import { ethers } from "hardhat"; +import { publicAbi } from "../test-helpers/helpers"; +import { assert, expect } from "chai"; +import { Signer, Contract, constants } from "ethers"; +import { Users, getUsers } from "../test-helpers/setup"; + +let users: Users; + +let owner: Signer; +let ownerAddress: string; +let aggregator: Signer; +let aggregatorAddress: string; +let validator: Signer; +let validatorAddress: string; +let validatorProxy: Contract; + +before(async () => { + users = await getUsers(); + owner = users.personas.Default; + aggregator = users.contracts.contract1; + validator = users.contracts.contract2; + ownerAddress = await owner.getAddress(); + aggregatorAddress = await aggregator.getAddress(); + validatorAddress = await validator.getAddress(); +}); + +describe("ValidatorProxy", () => { + beforeEach(async () => { + const vpf = await ethers.getContractFactory("ValidatorProxy", owner); + validatorProxy = await vpf.deploy(aggregatorAddress, validatorAddress); + validatorProxy = await validatorProxy.deployed(); + }); + + it("has a limited public interface", async () => { + publicAbi(validatorProxy, [ + // ConfirmedOwner functions + "acceptOwnership", + "owner", + "transferOwnership", + // ValidatorProxy functions + "validate", + "proposeNewAggregator", + "upgradeAggregator", + "getAggregators", + "proposeNewValidator", + "upgradeValidator", + "getValidators", + "typeAndVersion", + ]); + }); + + describe("#constructor", () => { + it("should set the aggregator addresses correctly", async () => { + const response = await validatorProxy.getAggregators(); + assert.equal(response.current, aggregatorAddress); + assert.equal(response.hasProposal, false); + assert.equal(response.proposed, constants.AddressZero); + }); + + it("should set the validator addresses conrrectly", async () => { + const response = await validatorProxy.getValidators(); + assert.equal(response.current, validatorAddress); + assert.equal(response.hasProposal, false); + assert.equal(response.proposed, constants.AddressZero); + }); + + it("should set the owner correctly", async () => { + const response = await validatorProxy.owner(); + assert.equal(response, ownerAddress); + }); + }); + + describe("#proposeNewAggregator", () => { + let newAggregator: Signer; + let newAggregatorAddress: string; + beforeEach(async () => { + newAggregator = users.contracts.contract3; + newAggregatorAddress = await newAggregator.getAddress(); + }); + + describe("failure", () => { + it("should only be called by the owner", async () => { + const stranger = users.contracts.contract4; + await expect(validatorProxy.connect(stranger).proposeNewAggregator(newAggregatorAddress)).to.be.revertedWith( + "Only callable by owner", + ); + }); + + it("should revert if no change in proposal", async () => { + await validatorProxy.proposeNewAggregator(newAggregatorAddress); + await expect(validatorProxy.proposeNewAggregator(newAggregatorAddress)).to.be.revertedWith("Invalid proposal"); + }); + + it("should revert if the proposal is the same as the current", async () => { + await expect(validatorProxy.proposeNewAggregator(aggregatorAddress)).to.be.revertedWith("Invalid proposal"); + }); + }); + + describe("success", () => { + it("should emit an event", async () => { + await expect(validatorProxy.proposeNewAggregator(newAggregatorAddress)) + .to.emit(validatorProxy, "AggregatorProposed") + .withArgs(newAggregatorAddress); + }); + + it("should set the correct address and hasProposal is true", async () => { + await validatorProxy.proposeNewAggregator(newAggregatorAddress); + const response = await validatorProxy.getAggregators(); + assert.equal(response.current, aggregatorAddress); + assert.equal(response.hasProposal, true); + assert.equal(response.proposed, newAggregatorAddress); + }); + + it("should set a zero address and hasProposal is false", async () => { + await validatorProxy.proposeNewAggregator(newAggregatorAddress); + await validatorProxy.proposeNewAggregator(constants.AddressZero); + const response = await validatorProxy.getAggregators(); + assert.equal(response.current, aggregatorAddress); + assert.equal(response.hasProposal, false); + assert.equal(response.proposed, constants.AddressZero); + }); + }); + }); + + describe("#upgradeAggregator", () => { + describe("failure", () => { + it("should only be called by the owner", async () => { + const stranger = users.contracts.contract4; + await expect(validatorProxy.connect(stranger).upgradeAggregator()).to.be.revertedWith("Only callable by owner"); + }); + + it("should revert if there is no proposal", async () => { + await expect(validatorProxy.upgradeAggregator()).to.be.revertedWith("No proposal"); + }); + }); + + describe("success", () => { + let newAggregator: Signer; + let newAggregatorAddress: string; + beforeEach(async () => { + newAggregator = users.contracts.contract3; + newAggregatorAddress = await newAggregator.getAddress(); + await validatorProxy.proposeNewAggregator(newAggregatorAddress); + }); + + it("should emit an event", async () => { + await expect(validatorProxy.upgradeAggregator()) + .to.emit(validatorProxy, "AggregatorUpgraded") + .withArgs(aggregatorAddress, newAggregatorAddress); + }); + + it("should upgrade the addresses", async () => { + await validatorProxy.upgradeAggregator(); + const response = await validatorProxy.getAggregators(); + assert.equal(response.current, newAggregatorAddress); + assert.equal(response.hasProposal, false); + assert.equal(response.proposed, constants.AddressZero); + }); + }); + }); + + describe("#proposeNewValidator", () => { + let newValidator: Signer; + let newValidatorAddress: string; + + beforeEach(async () => { + newValidator = users.contracts.contract3; + newValidatorAddress = await newValidator.getAddress(); + }); + + describe("failure", () => { + it("should only be called by the owner", async () => { + const stranger = users.contracts.contract4; + await expect(validatorProxy.connect(stranger).proposeNewAggregator(newValidatorAddress)).to.be.revertedWith( + "Only callable by owner", + ); + }); + + it("should revert if no change in proposal", async () => { + await validatorProxy.proposeNewValidator(newValidatorAddress); + await expect(validatorProxy.proposeNewValidator(newValidatorAddress)).to.be.revertedWith("Invalid proposal"); + }); + + it("should revert if the proposal is the same as the current", async () => { + await expect(validatorProxy.proposeNewValidator(validatorAddress)).to.be.revertedWith("Invalid proposal"); + }); + }); + + describe("success", () => { + it("should emit an event", async () => { + await expect(validatorProxy.proposeNewValidator(newValidatorAddress)) + .to.emit(validatorProxy, "ValidatorProposed") + .withArgs(newValidatorAddress); + }); + + it("should set the correct address and hasProposal is true", async () => { + await validatorProxy.proposeNewValidator(newValidatorAddress); + const response = await validatorProxy.getValidators(); + assert.equal(response.current, validatorAddress); + assert.equal(response.hasProposal, true); + assert.equal(response.proposed, newValidatorAddress); + }); + + it("should set a zero address and hasProposal is false", async () => { + await validatorProxy.proposeNewValidator(newValidatorAddress); + await validatorProxy.proposeNewValidator(constants.AddressZero); + const response = await validatorProxy.getValidators(); + assert.equal(response.current, validatorAddress); + assert.equal(response.hasProposal, false); + assert.equal(response.proposed, constants.AddressZero); + }); + }); + }); + + describe("#upgradeValidator", () => { + describe("failure", () => { + it("should only be called by the owner", async () => { + const stranger = users.contracts.contract4; + await expect(validatorProxy.connect(stranger).upgradeValidator()).to.be.revertedWith("Only callable by owner"); + }); + + it("should revert if there is no proposal", async () => { + await expect(validatorProxy.upgradeValidator()).to.be.revertedWith("No proposal"); + }); + }); + + describe("success", () => { + let newValidator: Signer; + let newValidatorAddress: string; + beforeEach(async () => { + newValidator = users.contracts.contract3; + newValidatorAddress = await newValidator.getAddress(); + await validatorProxy.proposeNewValidator(newValidatorAddress); + }); + + it("should emit an event", async () => { + await expect(validatorProxy.upgradeValidator()) + .to.emit(validatorProxy, "ValidatorUpgraded") + .withArgs(validatorAddress, newValidatorAddress); + }); + + it("should upgrade the addresses", async () => { + await validatorProxy.upgradeValidator(); + const response = await validatorProxy.getValidators(); + assert.equal(response.current, newValidatorAddress); + assert.equal(response.hasProposal, false); + assert.equal(response.proposed, constants.AddressZero); + }); + }); + }); + + describe("#validate", () => { + describe("failure", () => { + it("reverts when not called by aggregator or proposed aggregator", async () => { + const stranger = users.contracts.contract5; + await expect(validatorProxy.connect(stranger).validate(99, 88, 77, 66)).to.be.revertedWith( + "Not a configured aggregator", + ); + }); + + it("reverts when there is no validator set", async () => { + const vpf = await ethers.getContractFactory("ValidatorProxy", owner); + validatorProxy = await vpf.deploy(aggregatorAddress, constants.AddressZero); + await validatorProxy.deployed(); + await expect(validatorProxy.connect(aggregator).validate(99, 88, 77, 66)).to.be.revertedWith( + "No validator set", + ); + }); + }); + + describe("success", () => { + describe("from the aggregator", () => { + let mockValidator1: Contract; + beforeEach(async () => { + const mvf = await ethers.getContractFactory("MockAggregatorValidator", owner); + mockValidator1 = await mvf.deploy(1); + mockValidator1 = await mockValidator1.deployed(); + const vpf = await ethers.getContractFactory("ValidatorProxy", owner); + validatorProxy = await vpf.deploy(aggregatorAddress, mockValidator1.address); + validatorProxy = await validatorProxy.deployed(); + }); + + describe("for a single validator", () => { + it("calls validate on the validator", async () => { + await expect(validatorProxy.connect(aggregator).validate(200, 300, 400, 500)) + .to.emit(mockValidator1, "ValidateCalled") + .withArgs(1, 200, 300, 400, 500); + }); + + it("uses a specific amount of gas", async () => { + const resp = await validatorProxy.connect(aggregator).validate(200, 300, 400, 500); + const receipt = await resp.wait(); + assert.equal(receipt.gasUsed.toString(), "32406"); + }); + }); + + describe("for a validator and a proposed validator", () => { + let mockValidator2: Contract; + + beforeEach(async () => { + const mvf = await ethers.getContractFactory("MockAggregatorValidator", owner); + mockValidator2 = await mvf.deploy(2); + mockValidator2 = await mockValidator2.deployed(); + await validatorProxy.proposeNewValidator(mockValidator2.address); + }); + + it("calls validate on the validator", async () => { + await expect(validatorProxy.connect(aggregator).validate(2000, 3000, 4000, 5000)) + .to.emit(mockValidator1, "ValidateCalled") + .withArgs(1, 2000, 3000, 4000, 5000); + }); + + it("also calls validate on the proposed validator", async () => { + await expect(validatorProxy.connect(aggregator).validate(2000, 3000, 4000, 5000)) + .to.emit(mockValidator2, "ValidateCalled") + .withArgs(2, 2000, 3000, 4000, 5000); + }); + + it("uses a specific amount of gas", async () => { + const resp = await validatorProxy.connect(aggregator).validate(2000, 3000, 4000, 5000); + const receipt = await resp.wait(); + assert.equal(receipt.gasUsed.toString(), "40495"); + }); + }); + }); + + describe("from the proposed aggregator", () => { + let newAggregator: Signer; + let newAggregatorAddress: string; + beforeEach(async () => { + newAggregator = users.contracts.contract3; + newAggregatorAddress = await newAggregator.getAddress(); + await validatorProxy.connect(owner).proposeNewAggregator(newAggregatorAddress); + }); + + it("emits an event", async () => { + await expect(validatorProxy.connect(newAggregator).validate(555, 666, 777, 888)) + .to.emit(validatorProxy, "ProposedAggregatorValidateCall") + .withArgs(newAggregatorAddress, 555, 666, 777, 888); + }); + }); + }); + }); +}); diff --git a/lib/chainlink-brownie-contracts/contracts/tsconfig.ethers.json b/lib/chainlink-brownie-contracts/contracts/tsconfig.ethers.json new file mode 100644 index 0000000..7ed9517 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/tsconfig.ethers.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.cjs.json", + "include": ["ethers"] +} diff --git a/lib/chainlink-brownie-contracts/contracts/tsconfig.json b/lib/chainlink-brownie-contracts/contracts/tsconfig.json new file mode 100644 index 0000000..1f01889 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.cjs.json", + "compilerOptions": { + "noEmit": true + }, + "exclude": ["node_modules"] +} diff --git a/lib/chainlink-brownie-contracts/contracts/tsconfig.test.json b/lib/chainlink-brownie-contracts/contracts/tsconfig.test.json new file mode 100644 index 0000000..5375633 --- /dev/null +++ b/lib/chainlink-brownie-contracts/contracts/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.cjs.json", + "include": ["test/v0.4", "test/v0.5", "test/v0.6", "test/v0.7", "ethers"], + "compilerOptions": { + "noEmit": true + }, + "references": [{ "path": "../evm-test-helpers" }] +} diff --git a/lib/chainlink-brownie-contracts/package.json b/lib/chainlink-brownie-contracts/package.json new file mode 100644 index 0000000..a89eb92 --- /dev/null +++ b/lib/chainlink-brownie-contracts/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@chainlink/contracts": "^0.5.1" + } +} diff --git a/lib/chainlink-brownie-contracts/version.txt b/lib/chainlink-brownie-contracts/version.txt new file mode 100644 index 0000000..4b9fcbe --- /dev/null +++ b/lib/chainlink-brownie-contracts/version.txt @@ -0,0 +1 @@ +0.5.1 diff --git a/lib/chainlink-brownie-contracts/yarn.lock b/lib/chainlink-brownie-contracts/yarn.lock new file mode 100644 index 0000000..8fad359 --- /dev/null +++ b/lib/chainlink-brownie-contracts/yarn.lock @@ -0,0 +1,429 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@chainlink/contracts@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@chainlink/contracts/-/contracts-0.5.1.tgz#68e7447ba8c1eccfbb760bacc93aced2eef60945" + integrity sha512-3PDBJ38Sd6Ml9h7FNK/tZQti+kTCdXUq1qzE6E59CnlzycsV9ElPvf2hTvs9Mi9C6pEx2Mmw9yhZMfBktYUInQ== + dependencies: + "@eth-optimism/contracts" "^0.5.21" + "@openzeppelin/contracts" "^4.3.3" + "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2" + +"@eth-optimism/contracts@^0.5.21": + version "0.5.36" + resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.36.tgz#993be54587f1a1ca78d41ad92fb7314500a45095" + integrity sha512-yOXtp/MBQFHPGbB7sGmMB6A6dxuKj8Figj2aS/5gJVXyV6wKPsvbQI5EBG3AE31qdi5C52lUqlR5EsJyMhqWBw== + dependencies: + "@eth-optimism/core-utils" "0.10.1" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + +"@eth-optimism/core-utils@0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.10.1.tgz#44515fbca627532a24c6fd433395f00be8525832" + integrity sha512-IJvG5UtYvyz6An9QdohlCLoeB3NBFxx2lRJKlPzvYYlfugUNNCHsajRIWIwJTcPRRma0WPd46JUsKACLJDdNrA== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/contracts" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/providers" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bufio "^1.0.7" + chai "^4.3.4" + +"@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.1.tgz#b0799b616d5579cd1067a8ebf1fc1ec74c1e122c" + integrity sha512-vZveG/DLyo+wk4Ga1yx6jSEHrLPgmTt+dFv0dv8URpVCRf0jVhalps1jq/emN/oXnMRsC7cQgAF32DcXLL7BPQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@openzeppelin/contracts-v0.7@npm:@openzeppelin/contracts@v3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" + integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== + +"@openzeppelin/contracts@^4.3.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" + integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +bufio@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.0.7.tgz#b7f63a1369a0829ed64cc14edf0573b3e382a33e" + integrity sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A== + +chai@^4.3.4: + version "4.3.6" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" + integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +loupe@^2.3.1: + version "2.3.4" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" + integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== + dependencies: + get-func-name "^2.0.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== diff --git a/lib/forge-std/.github/workflows/tests.yml b/lib/forge-std/.github/workflows/tests.yml new file mode 100644 index 0000000..8e86b25 --- /dev/null +++ b/lib/forge-std/.github/workflows/tests.yml @@ -0,0 +1,27 @@ +name: Tests +on: [push, pull_request] + +jobs: + check: + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install dependencies + run: forge install + - name: Run tests + run: forge test -vvv + - name: Build Test with older solc versions + run: | + forge build --contracts src/Test.sol --use solc:0.8.0 + forge build --contracts src/Test.sol --use solc:0.7.6 + forge build --contracts src/Test.sol --use solc:0.7.0 + forge build --contracts src/Test.sol --use solc:0.6.0 diff --git a/lib/forge-std/.gitignore b/lib/forge-std/.gitignore new file mode 100644 index 0000000..999e4a7 --- /dev/null +++ b/lib/forge-std/.gitignore @@ -0,0 +1,4 @@ +cache/ +out/ +.vscode +.idea \ No newline at end of file diff --git a/lib/forge-std/.gitmodules b/lib/forge-std/.gitmodules new file mode 100644 index 0000000..e124719 --- /dev/null +++ b/lib/forge-std/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/ds-test"] + path = lib/ds-test + url = https://github.com/dapphub/ds-test diff --git a/lib/forge-std/LICENSE-APACHE b/lib/forge-std/LICENSE-APACHE new file mode 100644 index 0000000..28d22de --- /dev/null +++ b/lib/forge-std/LICENSE-APACHE @@ -0,0 +1,203 @@ +Copyright Contributors to forge-std + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/forge-std/LICENSE-MIT b/lib/forge-std/LICENSE-MIT new file mode 100644 index 0000000..1538ed3 --- /dev/null +++ b/lib/forge-std/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright Contributors to forge-std + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER +DEALINGS IN THE SOFTWARE.R diff --git a/lib/forge-std/README.md b/lib/forge-std/README.md new file mode 100644 index 0000000..67dc160 --- /dev/null +++ b/lib/forge-std/README.md @@ -0,0 +1,246 @@ +# Forge Standard Library • [![tests](https://github.com/brockelmore/forge-std/actions/workflows/tests.yml/badge.svg)](https://github.com/brockelmore/forge-std/actions/workflows/tests.yml) + +Forge Standard Library is a collection of helpful contracts for use with [`forge` and `foundry`](https://github.com/foundry-rs/foundry). It leverages `forge`'s cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes. + +**Learn how to use Forge Std with the [📖 Foundry Book (Forge Std Guide)](https://book.getfoundry.sh/forge/forge-std.html).** + +## Install + +```bash +forge install foundry-rs/forge-std +``` + +## Contracts +### stdError + +This is a helper contract for errors and reverts. In `forge`, this contract is particularly helpful for the `expectRevert` cheatcode, as it provides all compiler builtin errors. + +See the contract itself for all error codes. + +#### Example usage + +```solidity + +import "forge-std/Test.sol"; + +contract TestContract is Test { + ErrorsTest test; + + function setUp() public { + test = new ErrorsTest(); + } + + function testExpectArithmetic() public { + vm.expectRevert(stdError.arithmeticError); + test.arithmeticError(10); + } +} + +contract ErrorsTest { + function arithmeticError(uint256 a) public { + uint256 a = a - 100; + } +} +``` + +### stdStorage + +This is a rather large contract due to all of the overloading to make the UX decent. Primarily, it is a wrapper around the `record` and `accesses` cheatcodes. It can *always* find and write the storage slot(s) associated with a particular variable without knowing the storage layout. The one _major_ caveat to this is while a slot can be found for packed storage variables, we can't write to that variable safely. If a user tries to write to a packed slot, the execution throws an error, unless it is uninitialized (`bytes32(0)`). + +This works by recording all `SLOAD`s and `SSTORE`s during a function call. If there is a single slot read or written to, it immediately returns the slot. Otherwise, behind the scenes, we iterate through and check each one (assuming the user passed in a `depth` parameter). If the variable is a struct, you can pass in a `depth` parameter which is basically the field depth. + +I.e.: +```solidity +struct T { + // depth 0 + uint256 a; + // depth 1 + uint256 b; +} +``` + +#### Example usage + +```solidity +import "forge-std/Test.sol"; + +contract TestContract is Test { + using stdStorage for StdStorage; + + Storage test; + + function setUp() public { + test = new Storage(); + } + + function testFindExists() public { + // Lets say we want to find the slot for the public + // variable `exists`. We just pass in the function selector + // to the `find` command + uint256 slot = stdstore.target(address(test)).sig("exists()").find(); + assertEq(slot, 0); + } + + function testWriteExists() public { + // Lets say we want to write to the slot for the public + // variable `exists`. We just pass in the function selector + // to the `checked_write` command + stdstore.target(address(test)).sig("exists()").checked_write(100); + assertEq(test.exists(), 100); + } + + // It supports arbitrary storage layouts, like assembly based storage locations + function testFindHidden() public { + // `hidden` is a random hash of a bytes, iteration through slots would + // not find it. Our mechanism does + // Also, you can use the selector instead of a string + uint256 slot = stdstore.target(address(test)).sig(test.hidden.selector).find(); + assertEq(slot, uint256(keccak256("my.random.var"))); + } + + // If targeting a mapping, you have to pass in the keys necessary to perform the find + // i.e.: + function testFindMapping() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_addr.selector) + .with_key(address(this)) + .find(); + // in the `Storage` constructor, we wrote that this address' value was 1 in the map + // so when we load the slot, we expect it to be 1 + assertEq(uint(vm.load(address(test), bytes32(slot))), 1); + } + + // If the target is a struct, you can specify the field depth: + function testFindStruct() public { + // NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc. + uint256 slot_for_a_field = stdstore + .target(address(test)) + .sig(test.basicStruct.selector) + .depth(0) + .find(); + + uint256 slot_for_b_field = stdstore + .target(address(test)) + .sig(test.basicStruct.selector) + .depth(1) + .find(); + + assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1); + assertEq(uint(vm.load(address(test), bytes32(slot_for_b_field))), 2); + } +} + +// A complex storage contract +contract Storage { + struct UnpackedStruct { + uint256 a; + uint256 b; + } + + constructor() { + map_addr[msg.sender] = 1; + } + + uint256 public exists = 1; + mapping(address => uint256) public map_addr; + // mapping(address => Packed) public map_packed; + mapping(address => UnpackedStruct) public map_struct; + mapping(address => mapping(address => uint256)) public deep_map; + mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; + UnpackedStruct public basicStruct = UnpackedStruct({ + a: 1, + b: 2 + }); + + function hidden() public view returns (bytes32 t) { + // an extremely hidden storage slot + bytes32 slot = keccak256("my.random.var"); + assembly { + t := sload(slot) + } + } +} +``` + +### stdCheats + +This is a wrapper over miscellaneous cheatcodes that need wrappers to be more dev friendly. Currently there are only functions related to `prank`. In general, users may expect ETH to be put into an address on `prank`, but this is not the case for safety reasons. Explicitly this `hoax` function should only be used for address that have expected balances as it will get overwritten. If an address already has ETH, you should just use `prank`. If you want to change that balance explicitly, just use `deal`. If you want to do both, `hoax` is also right for you. + + +#### Example usage: +```solidity + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +// Inherit the stdCheats +contract StdCheatsTest is Test { + Bar test; + function setUp() public { + test = new Bar(); + } + + function testHoax() public { + // we call `hoax`, which gives the target address + // eth and then calls `prank` + hoax(address(1337)); + test.bar{value: 100}(address(1337)); + + // overloaded to allow you to specify how much eth to + // initialize the address with + hoax(address(1337), 1); + test.bar{value: 1}(address(1337)); + } + + function testStartHoax() public { + // we call `startHoax`, which gives the target address + // eth and then calls `startPrank` + // + // it is also overloaded so that you can specify an eth amount + startHoax(address(1337)); + test.bar{value: 100}(address(1337)); + test.bar{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } +} + +contract Bar { + function bar(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + } +} +``` + +### Std Assertions + +Expand upon the assertion functions from the `DSTest` library. + +### `console.log` + +Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log). +It's recommended to use `console2.sol` as shown below, as this will show the decoded logs in Forge traces. + +```solidity +// import it indirectly via Test.sol +import "forge-std/Test.sol"; +// or directly import it +import "forge-std/console2.sol"; +... +console2.log(someValue); +``` + +If you need compatibility with Hardhat, you must use the standard `console.sol` instead. +Due to a bug in `console.sol`, logs that use `uint256` or `int256` types will not be properly decoded in Forge traces. + +```solidity +// import it indirectly via Test.sol +import "forge-std/Test.sol"; +// or directly import it +import "forge-std/console.sol"; +... +console.log(someValue); +``` diff --git a/lib/forge-std/foundry.toml b/lib/forge-std/foundry.toml new file mode 100644 index 0000000..507b8bb --- /dev/null +++ b/lib/forge-std/foundry.toml @@ -0,0 +1,2 @@ +[profile.default] +fs_permissions = [{ access = "read-write", path = "./"}] diff --git a/lib/forge-std/lib/ds-test/.gitignore b/lib/forge-std/lib/ds-test/.gitignore new file mode 100644 index 0000000..63f0b2c --- /dev/null +++ b/lib/forge-std/lib/ds-test/.gitignore @@ -0,0 +1,3 @@ +/.dapple +/build +/out diff --git a/lib/forge-std/lib/ds-test/LICENSE b/lib/forge-std/lib/ds-test/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/forge-std/lib/ds-test/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/lib/forge-std/lib/ds-test/Makefile b/lib/forge-std/lib/ds-test/Makefile new file mode 100644 index 0000000..661dac4 --- /dev/null +++ b/lib/forge-std/lib/ds-test/Makefile @@ -0,0 +1,14 @@ +all:; dapp build + +test: + -dapp --use solc:0.4.23 build + -dapp --use solc:0.4.26 build + -dapp --use solc:0.5.17 build + -dapp --use solc:0.6.12 build + -dapp --use solc:0.7.5 build + +demo: + DAPP_SRC=demo dapp --use solc:0.7.5 build + -hevm dapp-test --verbose 3 + +.PHONY: test demo diff --git a/lib/forge-std/lib/ds-test/default.nix b/lib/forge-std/lib/ds-test/default.nix new file mode 100644 index 0000000..cf65419 --- /dev/null +++ b/lib/forge-std/lib/ds-test/default.nix @@ -0,0 +1,4 @@ +{ solidityPackage, dappsys }: solidityPackage { + name = "ds-test"; + src = ./src; +} diff --git a/lib/forge-std/lib/ds-test/demo/demo.sol b/lib/forge-std/lib/ds-test/demo/demo.sol new file mode 100644 index 0000000..f3bb48e --- /dev/null +++ b/lib/forge-std/lib/ds-test/demo/demo.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.5.0; + +import "../src/test.sol"; + +contract DemoTest is DSTest { + function test_this() public pure { + require(true); + } + function test_logs() public { + emit log("-- log(string)"); + emit log("a string"); + + emit log("-- log_named_uint(string, uint)"); + emit log_named_uint("uint", 512); + + emit log("-- log_named_int(string, int)"); + emit log_named_int("int", -512); + + emit log("-- log_named_address(string, address)"); + emit log_named_address("address", address(this)); + + emit log("-- log_named_bytes32(string, bytes32)"); + emit log_named_bytes32("bytes32", "a string"); + + emit log("-- log_named_bytes(string, bytes)"); + emit log_named_bytes("bytes", hex"cafefe"); + + emit log("-- log_named_string(string, string)"); + emit log_named_string("string", "a string"); + + emit log("-- log_named_decimal_uint(string, uint, uint)"); + emit log_named_decimal_uint("decimal uint", 1.0e18, 18); + + emit log("-- log_named_decimal_int(string, int, uint)"); + emit log_named_decimal_int("decimal int", -1.0e18, 18); + } + event log_old_named_uint(bytes32,uint); + function test_old_logs() public { + emit log_old_named_uint("key", 500); + emit log_named_bytes32("bkey", "val"); + } + function test_trace() public view { + this.echo("string 1", "string 2"); + } + function test_multiline() public { + emit log("a multiline\\nstring"); + emit log("a multiline string"); + emit log_bytes("a string"); + emit log_bytes("a multiline\nstring"); + emit log_bytes("a multiline\\nstring"); + emit logs(hex"0000"); + emit log_named_bytes("0x0000", hex"0000"); + emit logs(hex"ff"); + } + function echo(string memory s1, string memory s2) public pure + returns (string memory, string memory) + { + return (s1, s2); + } + + function prove_this(uint x) public { + emit log_named_uint("sym x", x); + assertGt(x + 1, 0); + } + + function test_logn() public { + assembly { + log0(0x01, 0x02) + log1(0x01, 0x02, 0x03) + log2(0x01, 0x02, 0x03, 0x04) + log3(0x01, 0x02, 0x03, 0x04, 0x05) + } + } + + event MyEvent(uint, uint indexed, uint, uint indexed); + function test_events() public { + emit MyEvent(1, 2, 3, 4); + } + + function test_asserts() public { + string memory err = "this test has failed!"; + emit log("## assertTrue(bool)\n"); + assertTrue(false); + emit log("\n"); + assertTrue(false, err); + + emit log("\n## assertEq(address,address)\n"); + assertEq(address(this), msg.sender); + emit log("\n"); + assertEq(address(this), msg.sender, err); + + emit log("\n## assertEq32(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(uint,uint)\n"); + assertEq(uint(0), 1); + emit log("\n"); + assertEq(uint(0), 1, err); + + emit log("\n## assertEq(int,int)\n"); + assertEq(-1, -2); + emit log("\n"); + assertEq(-1, -2, err); + + emit log("\n## assertEqDecimal(int,int,uint)\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertEqDecimal(uint,uint,uint)\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGt(uint,uint)\n"); + assertGt(uint(0), 0); + emit log("\n"); + assertGt(uint(0), 0, err); + + emit log("\n## assertGt(int,int)\n"); + assertGt(-1, -1); + emit log("\n"); + assertGt(-1, -1, err); + + emit log("\n## assertGtDecimal(int,int,uint)\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGtDecimal(uint,uint,uint)\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGe(uint,uint)\n"); + assertGe(uint(0), 1); + emit log("\n"); + assertGe(uint(0), 1, err); + + emit log("\n## assertGe(int,int)\n"); + assertGe(-1, 0); + emit log("\n"); + assertGe(-1, 0, err); + + emit log("\n## assertGeDecimal(int,int,uint)\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGeDecimal(uint,uint,uint)\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertLt(uint,uint)\n"); + assertLt(uint(0), 0); + emit log("\n"); + assertLt(uint(0), 0, err); + + emit log("\n## assertLt(int,int)\n"); + assertLt(-1, -1); + emit log("\n"); + assertLt(-1, -1, err); + + emit log("\n## assertLtDecimal(int,int,uint)\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLtDecimal(uint,uint,uint)\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertLe(uint,uint)\n"); + assertLe(uint(1), 0); + emit log("\n"); + assertLe(uint(1), 0, err); + + emit log("\n## assertLe(int,int)\n"); + assertLe(0, -1); + emit log("\n"); + assertLe(0, -1, err); + + emit log("\n## assertLeDecimal(int,int,uint)\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLeDecimal(uint,uint,uint)\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertEq(string,string)\n"); + string memory s1 = "string 1"; + string memory s2 = "string 2"; + assertEq(s1, s2); + emit log("\n"); + assertEq(s1, s2, err); + + emit log("\n## assertEq0(bytes,bytes)\n"); + assertEq0(hex"abcdef01", hex"abcdef02"); + emit log("\n"); + assertEq0(hex"abcdef01", hex"abcdef02", err); + } +} + +contract DemoTestWithSetUp { + function setUp() public { + } + function test_pass() public pure { + } +} diff --git a/lib/forge-std/lib/ds-test/src/test.sol b/lib/forge-std/lib/ds-test/src/test.sol new file mode 100644 index 0000000..515a3bd --- /dev/null +++ b/lib/forge-std/lib/ds-test/src/test.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity >=0.5.0; + +contract DSTest { + event log (string); + event logs (bytes); + + event log_address (address); + event log_bytes32 (bytes32); + event log_int (int); + event log_uint (uint); + event log_bytes (bytes); + event log_string (string); + + event log_named_address (string key, address val); + event log_named_bytes32 (string key, bytes32 val); + event log_named_decimal_int (string key, int val, uint decimals); + event log_named_decimal_uint (string key, uint val, uint decimals); + event log_named_int (string key, int val); + event log_named_uint (string key, uint val); + event log_named_bytes (string key, bytes val); + event log_named_string (string key, string val); + + bool public IS_TEST = true; + bool private _failed; + + address constant HEVM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + modifier mayRevert() { _; } + modifier testopts(string memory) { _; } + + function failed() public returns (bool) { + if (_failed) { + return _failed; + } else { + bool globalFailed = false; + if (hasHEVMContext()) { + (, bytes memory retdata) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("load(address,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed")) + ) + ); + globalFailed = abi.decode(retdata, (bool)); + } + return globalFailed; + } + } + + function fail() internal { + if (hasHEVMContext()) { + (bool status, ) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("store(address,bytes32,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) + ) + ); + status; // Silence compiler warnings + } + _failed = true; + } + + function hasHEVMContext() internal view returns (bool) { + uint256 hevmCodeSize = 0; + assembly { + hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) + } + return hevmCodeSize > 0; + } + + modifier logs_gas() { + uint startGas = gasleft(); + _; + uint endGas = gasleft(); + emit log_named_uint("gas", startGas - endGas); + } + + function assertTrue(bool condition) internal { + if (!condition) { + emit log("Error: Assertion Failed"); + fail(); + } + } + + function assertTrue(bool condition, string memory err) internal { + if (!condition) { + emit log_named_string("Error", err); + assertTrue(condition); + } + } + + function assertEq(address a, address b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [address]"); + emit log_named_address(" Expected", b); + emit log_named_address(" Actual", a); + fail(); + } + } + function assertEq(address a, address b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes32 a, bytes32 b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [bytes32]"); + emit log_named_bytes32(" Expected", b); + emit log_named_bytes32(" Actual", a); + fail(); + } + } + function assertEq(bytes32 a, bytes32 b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + function assertEq32(bytes32 a, bytes32 b) internal { + assertEq(a, b); + } + function assertEq32(bytes32 a, bytes32 b, string memory err) internal { + assertEq(a, b, err); + } + + function assertEq(int a, int b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + fail(); + } + } + function assertEq(int a, int b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEq(uint a, uint b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + fail(); + } + } + function assertEq(uint a, uint b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEqDecimal(int a, int b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal int]"); + emit log_named_decimal_int(" Expected", b, decimals); + emit log_named_decimal_int(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + function assertEqDecimal(uint a, uint b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Expected", b, decimals); + emit log_named_decimal_uint(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + + function assertGt(uint a, uint b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGt(uint a, uint b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGt(int a, int b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGt(int a, int b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGtDecimal(int a, int b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + function assertGtDecimal(uint a, uint b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + + function assertGe(uint a, uint b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGe(uint a, uint b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGe(int a, int b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGe(int a, int b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGeDecimal(int a, int b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + function assertGeDecimal(uint a, uint b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertLt(uint a, uint b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLt(uint a, uint b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLt(int a, int b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLt(int a, int b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLtDecimal(int a, int b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + function assertLtDecimal(uint a, uint b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + + function assertLe(uint a, uint b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLe(uint a, uint b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLe(int a, int b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLe(int a, int b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLeDecimal(int a, int b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } + } + function assertLeDecimal(uint a, uint b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log("Error: a == b not satisfied [string]"); + emit log_named_string(" Expected", b); + emit log_named_string(" Actual", a); + fail(); + } + } + function assertEq(string memory a, string memory b, string memory err) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { + ok = true; + if (a.length == b.length) { + for (uint i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + ok = false; + } + } + } else { + ok = false; + } + } + function assertEq0(bytes memory a, bytes memory b) internal { + if (!checkEq0(a, b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Expected", b); + emit log_named_bytes(" Actual", a); + fail(); + } + } + function assertEq0(bytes memory a, bytes memory b, string memory err) internal { + if (!checkEq0(a, b)) { + emit log_named_string("Error", err); + assertEq0(a, b); + } + } +} diff --git a/lib/forge-std/package.json b/lib/forge-std/package.json new file mode 100644 index 0000000..914a361 --- /dev/null +++ b/lib/forge-std/package.json @@ -0,0 +1,16 @@ +{ + "name": "forge-std", + "version": "0.1.0", + "description": "Forge Standard Library is a collection of helpful contracts for use with forge and foundry", + "homepage": "https://book.getfoundry.sh/forge/forge-std", + "bugs": "https://github.com/foundry-rs/forge-std/issues", + "license": "(Apache-2.0 OR MIT)", + "author": "Contributors to forge-std", + "files": [ + "src/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/foundry-rs/forge-std.git" + } +} diff --git a/lib/forge-std/src/Script.sol b/lib/forge-std/src/Script.sol new file mode 100644 index 0000000..e1e3a51 --- /dev/null +++ b/lib/forge-std/src/Script.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; + +import "./console.sol"; +import "./console2.sol"; +import "./StdJson.sol"; + +abstract contract Script { + bool public IS_SCRIPT = true; + address constant private VM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + Vm public constant vm = Vm(VM_ADDRESS); + + /// @dev Compute the address a contract will be deployed at for a given deployer address and nonce + /// @notice adapated from Solmate implementation (https://github.com/transmissions11/solmate/blob/main/src/utils/LibRLP.sol) + function computeCreateAddress(address deployer, uint256 nonce) internal pure returns (address) { + // The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0. + // A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it. + if (nonce == 0x00) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80)))); + if (nonce <= 0x7f) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce)))); + + // Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length. + if (nonce <= 2**8 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce)))); + if (nonce <= 2**16 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce)))); + if (nonce <= 2**24 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce)))); + + // More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp + // 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + // 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex) + // We assume nobody can have a nonce large enough to require more than 32 bytes. + return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce)))); + } + + function addressFromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { + return address(uint160(uint256(bytesValue))); + } + + function deriveRememberKey(string memory mnemonic, uint32 index) internal returns (address who, uint256 privateKey) { + privateKey = vm.deriveKey(mnemonic, index); + who = vm.rememberKey(privateKey); + } +} diff --git a/lib/forge-std/src/StdJson.sol b/lib/forge-std/src/StdJson.sol new file mode 100644 index 0000000..c4ad825 --- /dev/null +++ b/lib/forge-std/src/StdJson.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./Vm.sol"; + +// Helpers for parsing keys into types. +library stdJson { + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function parseRaw(string memory json, string memory key) + internal + returns (bytes memory) + { + return vm.parseJson(json, key); + } + + function readUint(string memory json, string memory key) + internal + returns (uint256) + { + return abi.decode(vm.parseJson(json, key), (uint256)); + } + + function readUintArray(string memory json, string memory key) + internal + returns (uint256[] memory) + { + return abi.decode(vm.parseJson(json, key), (uint256[])); + } + + function readInt(string memory json, string memory key) + internal + returns (int256) + { + return abi.decode(vm.parseJson(json, key), (int256)); + } + + function readIntArray(string memory json, string memory key) + internal + returns (int256[] memory) + { + return abi.decode(vm.parseJson(json, key), (int256[])); + } + + function readBytes32(string memory json, string memory key) + internal + returns (bytes32) + { + return abi.decode(vm.parseJson(json, key), (bytes32)); + } + + function readBytes32Array(string memory json, string memory key) + internal + returns (bytes32[] memory) + { + return abi.decode(vm.parseJson(json, key), (bytes32[])); + } + + function readString(string memory json, string memory key) + internal + returns (string memory) + { + return abi.decode(vm.parseJson(json, key), (string)); + } + + function readStringArray(string memory json, string memory key) + internal + returns (string[] memory) + { + return abi.decode(vm.parseJson(json, key), (string[])); + } + + function readAddress(string memory json, string memory key) + internal + returns (address) + { + return abi.decode(vm.parseJson(json, key), (address)); + } + + function readAddressArray(string memory json, string memory key) + internal + returns (address[] memory) + { + return abi.decode(vm.parseJson(json, key), (address[])); + } + + function readBool(string memory json, string memory key) + internal + returns (bool) + { + return abi.decode(vm.parseJson(json, key), (bool)); + } + + function readBoolArray(string memory json, string memory key) + internal + returns (bool[] memory) + { + return abi.decode(vm.parseJson(json, key), (bool[])); + } + + function readBytes(string memory json, string memory key) + internal + returns (bytes memory) + { + return abi.decode(vm.parseJson(json, key), (bytes)); + } + + function readBytesArray(string memory json, string memory key) + internal + returns (bytes[] memory) + { + return abi.decode(vm.parseJson(json, key), (bytes[])); + } + + +} diff --git a/lib/forge-std/src/Test.sol b/lib/forge-std/src/Test.sol new file mode 100644 index 0000000..ef18bb6 --- /dev/null +++ b/lib/forge-std/src/Test.sol @@ -0,0 +1,1138 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./Script.sol"; +import "ds-test/test.sol"; + +// Wrappers around Cheatcodes to avoid footguns +abstract contract Test is DSTest, Script { + using stdStorage for StdStorage; + + uint256 internal constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; + + StdStorage internal stdstore; + + /*////////////////////////////////////////////////////////////////////////// + STD-LOGS + //////////////////////////////////////////////////////////////////////////*/ + + event log_array(uint256[] val); + event log_array(int256[] val); + event log_array(address[] val); + event log_named_array(string key, uint256[] val); + event log_named_array(string key, int256[] val); + event log_named_array(string key, address[] val); + + /*////////////////////////////////////////////////////////////////////////// + STD-CHEATS + //////////////////////////////////////////////////////////////////////////*/ + + // Skip forward or rewind time by the specified number of seconds + function skip(uint256 time) internal { + vm.warp(block.timestamp + time); + } + + function rewind(uint256 time) internal { + vm.warp(block.timestamp - time); + } + + // Setup a prank from an address that has some ether + function hoax(address who) internal { + vm.deal(who, 1 << 128); + vm.prank(who); + } + + function hoax(address who, uint256 give) internal { + vm.deal(who, give); + vm.prank(who); + } + + function hoax(address who, address origin) internal { + vm.deal(who, 1 << 128); + vm.prank(who, origin); + } + + function hoax(address who, address origin, uint256 give) internal { + vm.deal(who, give); + vm.prank(who, origin); + } + + // Start perpetual prank from an address that has some ether + function startHoax(address who) internal { + vm.deal(who, 1 << 128); + vm.startPrank(who); + } + + function startHoax(address who, uint256 give) internal { + vm.deal(who, give); + vm.startPrank(who); + } + + // Start perpetual prank from an address that has some ether + // tx.origin is set to the origin parameter + function startHoax(address who, address origin) internal { + vm.deal(who, 1 << 128); + vm.startPrank(who, origin); + } + + function startHoax(address who, address origin, uint256 give) internal { + vm.deal(who, give); + vm.startPrank(who, origin); + } + + function changePrank(address who) internal { + vm.stopPrank(); + vm.startPrank(who); + } + + // creates a labeled address and the corresponding private key + function makeAddrAndKey(string memory name) internal returns(address addr, uint256 privateKey) { + privateKey = uint256(keccak256(abi.encodePacked(name))); + addr = vm.addr(privateKey); + vm.label(addr, name); + } + + // creates a labeled address + function makeAddr(string memory name) internal returns(address addr) { + (addr,) = makeAddrAndKey(name); + } + + // DEPRECATED: Use `deal` instead + function tip(address token, address to, uint256 give) internal { + emit log_named_string("WARNING", "Test tip(address,address,uint256): The `tip` stdcheat has been deprecated. Use `deal` instead."); + stdstore + .target(token) + .sig(0x70a08231) + .with_key(to) + .checked_write(give); + } + + // The same as Vm's `deal` + // Use the alternative signature for ERC20 tokens + function deal(address to, uint256 give) internal { + vm.deal(to, give); + } + + // Set the balance of an account for any ERC20 token + // Use the alternative signature to update `totalSupply` + function deal(address token, address to, uint256 give) internal { + deal(token, to, give, false); + } + + function deal(address token, address to, uint256 give, bool adjust) internal { + // get current balance + (, bytes memory balData) = token.call(abi.encodeWithSelector(0x70a08231, to)); + uint256 prevBal = abi.decode(balData, (uint256)); + + // update balance + stdstore + .target(token) + .sig(0x70a08231) + .with_key(to) + .checked_write(give); + + // update total supply + if(adjust){ + (, bytes memory totSupData) = token.call(abi.encodeWithSelector(0x18160ddd)); + uint256 totSup = abi.decode(totSupData, (uint256)); + if(give < prevBal) { + totSup -= (prevBal - give); + } else { + totSup += (give - prevBal); + } + stdstore + .target(token) + .sig(0x18160ddd) + .checked_write(totSup); + } + } + + function bound(uint256 x, uint256 min, uint256 max) internal virtual returns (uint256 result) { + require(min <= max, "Test bound(uint256,uint256,uint256): Max is less than min."); + + uint256 size = max - min; + + if (size == 0) + { + result = min; + } + else if (size == UINT256_MAX) + { + result = x; + } + else + { + ++size; // make `max` inclusive + uint256 mod = x % size; + result = min + mod; + } + + emit log_named_uint("Bound Result", result); + } + + // Deploy a contract by fetching the contract bytecode from + // the artifacts directory + // e.g. `deployCode(code, abi.encode(arg1,arg2,arg3))` + function deployCode(string memory what, bytes memory args) + internal + returns (address addr) + { + bytes memory bytecode = abi.encodePacked(vm.getCode(what), args); + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string,bytes): Deployment failed." + ); + } + + function deployCode(string memory what) + internal + returns (address addr) + { + bytes memory bytecode = vm.getCode(what); + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string): Deployment failed." + ); + } + + /// deploy contract with value on construction + function deployCode(string memory what, bytes memory args, uint256 val) + internal + returns (address addr) + { + bytes memory bytecode = abi.encodePacked(vm.getCode(what), args); + /// @solidity memory-safe-assembly + assembly { + addr := create(val, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string,bytes,uint256): Deployment failed." + ); + } + + function deployCode(string memory what, uint256 val) + internal + returns (address addr) + { + bytes memory bytecode = vm.getCode(what); + /// @solidity memory-safe-assembly + assembly { + addr := create(val, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string,uint256): Deployment failed." + ); + } + + /*////////////////////////////////////////////////////////////////////////// + STD-ASSERTIONS + //////////////////////////////////////////////////////////////////////////*/ + + function fail(string memory err) internal virtual { + emit log_named_string("Error", err); + fail(); + } + + function assertFalse(bool data) internal virtual { + assertTrue(!data); + } + + function assertFalse(bool data, string memory err) internal virtual { + assertTrue(!data, err); + } + + function assertEq(bool a, bool b) internal { + if (a != b) { + emit log ("Error: a == b not satisfied [bool]"); + emit log_named_string (" Expected", b ? "true" : "false"); + emit log_named_string (" Actual", a ? "true" : "false"); + fail(); + } + } + + function assertEq(bool a, bool b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes memory a, bytes memory b) internal { + assertEq0(a, b); + } + + function assertEq(bytes memory a, bytes memory b, string memory err) internal { + assertEq0(a, b, err); + } + + function assertEq(uint256[] memory a, uint256[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [uint[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(int256[] memory a, int256[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [int[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(address[] memory a, address[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [address[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(uint256[] memory a, uint256[] memory b, string memory err) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(int256[] memory a, int256[] memory b, string memory err) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + + function assertEq(address[] memory a, address[] memory b, string memory err) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEqUint(uint256 a, uint256 b) internal { + assertEq(uint256(a), uint256(b)); + } + + function assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log ("Error: a ~= b not satisfied [uint]"); + emit log_named_uint (" Expected", b); + emit log_named_uint (" Actual", a); + emit log_named_uint (" Max Delta", maxDelta); + emit log_named_uint (" Delta", delta); + fail(); + } + } + + function assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta, + string memory err + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log_named_string ("Error", err); + assertApproxEqAbs(a, b, maxDelta); + } + } + + function assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log ("Error: a ~= b not satisfied [int]"); + emit log_named_int (" Expected", b); + emit log_named_int (" Actual", a); + emit log_named_uint (" Max Delta", maxDelta); + emit log_named_uint (" Delta", delta); + fail(); + } + } + + function assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta, + string memory err + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log_named_string ("Error", err); + assertApproxEqAbs(a, b, maxDelta); + } + } + + function assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log ("Error: a ~= b not satisfied [uint]"); + emit log_named_uint (" Expected", b); + emit log_named_uint (" Actual", a); + emit log_named_decimal_uint (" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint (" % Delta", percentDelta, 18); + fail(); + } + } + + function assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100% + string memory err + ) internal virtual { + if (b == 0) return assertEq(a, b, err); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log_named_string ("Error", err); + assertApproxEqRel(a, b, maxPercentDelta); + } + } + + function assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log ("Error: a ~= b not satisfied [int]"); + emit log_named_int (" Expected", b); + emit log_named_int (" Actual", a); + emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint(" % Delta", percentDelta, 18); + fail(); + } + } + + function assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta, + string memory err + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log_named_string ("Error", err); + assertApproxEqRel(a, b, maxPercentDelta); + } + } + + /*////////////////////////////////////////////////////////////// + JSON PARSING + //////////////////////////////////////////////////////////////*/ + + // Data structures to parse Transaction objects from the broadcast artifact + // that conform to EIP1559. The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct RawTx1559 { + string[] arguments; + address contractAddress; + string contractName; + // json value name = function + string functionSig; + bytes32 hash; + // json value name = tx + RawTx1559Detail txDetail; + // json value name = type + string opcode; + } + + struct RawTx1559Detail { + AccessList[] accessList; + bytes data; + address from; + bytes gas; + bytes nonce; + address to; + bytes txType; + bytes value; + } + + struct Tx1559 { + string[] arguments; + address contractAddress; + string contractName; + string functionSig; + bytes32 hash; + Tx1559Detail txDetail; + string opcode; + } + + struct Tx1559Detail { + AccessList[] accessList; + bytes data; + address from; + uint256 gas; + uint256 nonce; + address to; + uint256 txType; + uint256 value; + } + + // Data structures to parse Transaction objects from the broadcast artifact + // that DO NOT conform to EIP1559. The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct TxLegacy{ + string[] arguments; + address contractAddress; + string contractName; + string functionSig; + string hash; + string opcode; + TxDetailLegacy transaction; + } + + struct TxDetailLegacy{ + AccessList[] accessList; + uint256 chainId; + bytes data; + address from; + uint256 gas; + uint256 gasPrice; + bytes32 hash; + uint256 nonce; + bytes1 opcode; + bytes32 r; + bytes32 s; + uint256 txType; + address to; + uint8 v; + uint256 value; + } + + struct AccessList{ + address accessAddress; + bytes32[] storageKeys; + } + + // Data structures to parse Receipt objects from the broadcast artifact. + // The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct RawReceipt { + bytes32 blockHash; + bytes blockNumber; + address contractAddress; + bytes cumulativeGasUsed; + bytes effectiveGasPrice; + address from; + bytes gasUsed; + RawReceiptLog[] logs; + bytes logsBloom; + bytes status; + address to; + bytes32 transactionHash; + bytes transactionIndex; + } + + struct Receipt { + bytes32 blockHash; + uint256 blockNumber; + address contractAddress; + uint256 cumulativeGasUsed; + uint256 effectiveGasPrice; + address from; + uint256 gasUsed; + ReceiptLog[] logs; + bytes logsBloom; + uint256 status; + address to; + bytes32 transactionHash; + uint256 transactionIndex; + } + + // Data structures to parse the entire broadcast artifact, assuming the + // transactions conform to EIP1559. + + struct EIP1559ScriptArtifact { + string[] libraries; + string path; + string[] pending; + Receipt[] receipts; + uint256 timestamp; + Tx1559[] transactions; + TxReturn[] txReturns; + } + + struct RawEIP1559ScriptArtifact { + string[] libraries; + string path; + string[] pending; + RawReceipt[] receipts; + TxReturn[] txReturns; + uint256 timestamp; + RawTx1559[] transactions; + } + + struct RawReceiptLog { + // json value = address + address logAddress; + bytes32 blockHash; + bytes blockNumber; + bytes data; + bytes logIndex; + bool removed; + bytes32[] topics; + bytes32 transactionHash; + bytes transactionIndex; + bytes transactionLogIndex; + } + + struct ReceiptLog { + // json value = address + address logAddress; + bytes32 blockHash; + uint256 blockNumber; + bytes data; + uint256 logIndex; + bytes32[] topics; + uint256 transactionIndex; + uint256 transactionLogIndex; + bool removed; + } + + struct TxReturn { + string internalType; + string value; + } + + + function readEIP1559ScriptArtifact(string memory path) + internal + returns(EIP1559ScriptArtifact memory) + { + string memory data = vm.readFile(path); + bytes memory parsedData = vm.parseJson(data); + RawEIP1559ScriptArtifact memory rawArtifact = abi.decode(parsedData, (RawEIP1559ScriptArtifact)); + EIP1559ScriptArtifact memory artifact; + artifact.libraries = rawArtifact.libraries; + artifact.path = rawArtifact.path; + artifact.timestamp = rawArtifact.timestamp; + artifact.pending = rawArtifact.pending; + artifact.txReturns = rawArtifact.txReturns; + artifact.receipts = rawToConvertedReceipts(rawArtifact.receipts); + artifact.transactions = rawToConvertedEIPTx1559s(rawArtifact.transactions); + return artifact; + } + + function rawToConvertedEIPTx1559s(RawTx1559[] memory rawTxs) + internal pure + returns (Tx1559[] memory) + { + Tx1559[] memory txs = new Tx1559[](rawTxs.length); + for (uint i; i < rawTxs.length; i++) { + txs[i] = rawToConvertedEIPTx1559(rawTxs[i]); + } + return txs; + } + + function rawToConvertedEIPTx1559(RawTx1559 memory rawTx) + internal pure + returns (Tx1559 memory) + { + Tx1559 memory transaction; + transaction.arguments = rawTx.arguments; + transaction.contractName = rawTx.contractName; + transaction.functionSig = rawTx.functionSig; + transaction.hash= rawTx.hash; + transaction.txDetail = rawToConvertedEIP1559Detail(rawTx.txDetail); + transaction.opcode= rawTx.opcode; + return transaction; + } + + function rawToConvertedEIP1559Detail(RawTx1559Detail memory rawDetail) + internal pure + returns (Tx1559Detail memory) + { + Tx1559Detail memory txDetail; + txDetail.data = rawDetail.data; + txDetail.from = rawDetail.from; + txDetail.to = rawDetail.to; + txDetail.nonce = bytesToUint(rawDetail.nonce); + txDetail.txType = bytesToUint(rawDetail.txType); + txDetail.value = bytesToUint(rawDetail.value); + txDetail.gas = bytesToUint(rawDetail.gas); + txDetail.accessList = rawDetail.accessList; + return txDetail; + + } + + function readTx1559s(string memory path) + internal + returns (Tx1559[] memory) + { + string memory deployData = vm.readFile(path); + bytes memory parsedDeployData = + vm.parseJson(deployData, ".transactions"); + RawTx1559[] memory rawTxs = abi.decode(parsedDeployData, (RawTx1559[])); + return rawToConvertedEIPTx1559s(rawTxs); + } + + + function readTx1559(string memory path, uint256 index) + internal + returns (Tx1559 memory) + { + string memory deployData = vm.readFile(path); + string memory key = string(abi.encodePacked(".transactions[",vm.toString(index), "]")); + bytes memory parsedDeployData = + vm.parseJson(deployData, key); + RawTx1559 memory rawTx = abi.decode(parsedDeployData, (RawTx1559)); + return rawToConvertedEIPTx1559(rawTx); + } + + + // Analogous to readTransactions, but for receipts. + function readReceipts(string memory path) + internal + returns (Receipt[] memory) + { + string memory deployData = vm.readFile(path); + bytes memory parsedDeployData = vm.parseJson(deployData, ".receipts"); + RawReceipt[] memory rawReceipts = abi.decode(parsedDeployData, (RawReceipt[])); + return rawToConvertedReceipts(rawReceipts); + } + + function readReceipt(string memory path, uint index) + internal + returns (Receipt memory) + { + string memory deployData = vm.readFile(path); + string memory key = string(abi.encodePacked(".receipts[",vm.toString(index), "]")); + bytes memory parsedDeployData = vm.parseJson(deployData, key); + RawReceipt memory rawReceipt = abi.decode(parsedDeployData, (RawReceipt)); + return rawToConvertedReceipt(rawReceipt); + } + + function rawToConvertedReceipts(RawReceipt[] memory rawReceipts) + internal pure + returns(Receipt[] memory) + { + Receipt[] memory receipts = new Receipt[](rawReceipts.length); + for (uint i; i < rawReceipts.length; i++) { + receipts[i] = rawToConvertedReceipt(rawReceipts[i]); + } + return receipts; + } + + function rawToConvertedReceipt(RawReceipt memory rawReceipt) + internal pure + returns(Receipt memory) + { + Receipt memory receipt; + receipt.blockHash = rawReceipt.blockHash; + receipt.to = rawReceipt.to; + receipt.from = rawReceipt.from; + receipt.contractAddress = rawReceipt.contractAddress; + receipt.effectiveGasPrice = bytesToUint(rawReceipt.effectiveGasPrice); + receipt.cumulativeGasUsed= bytesToUint(rawReceipt.cumulativeGasUsed); + receipt.gasUsed = bytesToUint(rawReceipt.gasUsed); + receipt.status = bytesToUint(rawReceipt.status); + receipt.transactionIndex = bytesToUint(rawReceipt.transactionIndex); + receipt.blockNumber = bytesToUint(rawReceipt.blockNumber); + receipt.logs = rawToConvertedReceiptLogs(rawReceipt.logs); + receipt.logsBloom = rawReceipt.logsBloom; + receipt.transactionHash = rawReceipt.transactionHash; + return receipt; + } + + function rawToConvertedReceiptLogs(RawReceiptLog[] memory rawLogs) + internal pure + returns (ReceiptLog[] memory) + { + ReceiptLog[] memory logs = new ReceiptLog[](rawLogs.length); + for (uint i; i < rawLogs.length; i++) { + logs[i].logAddress = rawLogs[i].logAddress; + logs[i].blockHash = rawLogs[i].blockHash; + logs[i].blockNumber = bytesToUint(rawLogs[i].blockNumber); + logs[i].data = rawLogs[i].data; + logs[i].logIndex = bytesToUint(rawLogs[i].logIndex); + logs[i].topics = rawLogs[i].topics; + logs[i].transactionIndex = bytesToUint(rawLogs[i].transactionIndex); + logs[i].transactionLogIndex = bytesToUint(rawLogs[i].transactionLogIndex); + logs[i].removed = rawLogs[i].removed; + } + return logs; + + } + + function bytesToUint(bytes memory b) internal pure returns (uint256){ + uint256 number; + for (uint i=0; i < b.length; i++) { + number = number + uint(uint8(b[i]))*(2**(8*(b.length-(i+1)))); + } + return number; + } + +} + +/*////////////////////////////////////////////////////////////////////////// + STD-ERRORS +//////////////////////////////////////////////////////////////////////////*/ + +library stdError { + bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); + bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); + bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); + bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); + bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); + bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); + bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); + bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); + bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); + // DEPRECATED: Use Vm's `expectRevert` without any arguments instead + bytes public constant lowLevelError = bytes(""); // `0x` +} + +/*////////////////////////////////////////////////////////////////////////// + STD-STORAGE +//////////////////////////////////////////////////////////////////////////*/ + +struct StdStorage { + mapping (address => mapping(bytes4 => mapping(bytes32 => uint256))) slots; + mapping (address => mapping(bytes4 => mapping(bytes32 => bool))) finds; + + bytes32[] _keys; + bytes4 _sig; + uint256 _depth; + address _target; + bytes32 _set; +} + +library stdStorage { + event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint slot); + event WARNING_UninitedSlot(address who, uint slot); + + uint256 private constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935; + int256 private constant INT256_MAX = 57896044618658097711785492504343953926634992332820282019728792003956564819967; + + Vm private constant vm_std_store = Vm(address(uint160(uint256(keccak256('hevm cheat code'))))); + + function sigs( + string memory sigStr + ) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(bytes(sigStr))); + } + + /// @notice find an arbitrary storage slot given a function sig, input data, address of the contract and a value to check against + // slot complexity: + // if flat, will be bytes32(uint256(uint)); + // if map, will be keccak256(abi.encode(key, uint(slot))); + // if deep map, will be keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot))))); + // if map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth); + function find( + StdStorage storage self + ) + internal + returns (uint256) + { + address who = self._target; + bytes4 fsig = self._sig; + uint256 field_depth = self._depth; + bytes32[] memory ins = self._keys; + + // calldata to test against + if (self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { + return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; + } + bytes memory cald = abi.encodePacked(fsig, flatten(ins)); + vm_std_store.record(); + bytes32 fdat; + { + (, bytes memory rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32*field_depth); + } + + (bytes32[] memory reads, ) = vm_std_store.accesses(address(who)); + if (reads.length == 1) { + bytes32 curr = vm_std_store.load(who, reads[0]); + if (curr == bytes32(0)) { + emit WARNING_UninitedSlot(who, uint256(reads[0])); + } + if (fdat != curr) { + require(false, "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported."); + } + emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[0])); + self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[0]); + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; + } else if (reads.length > 1) { + for (uint256 i = 0; i < reads.length; i++) { + bytes32 prev = vm_std_store.load(who, reads[i]); + if (prev == bytes32(0)) { + emit WARNING_UninitedSlot(who, uint256(reads[i])); + } + // store + vm_std_store.store(who, reads[i], bytes32(hex"1337")); + bool success; + bytes memory rdat; + { + (success, rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32*field_depth); + } + + if (success && fdat == bytes32(hex"1337")) { + // we found which of the slots is the actual one + emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[i])); + self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[i]); + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; + vm_std_store.store(who, reads[i], prev); + break; + } + vm_std_store.store(who, reads[i], prev); + } + } else { + require(false, "stdStorage find(StdStorage): No storage use detected for target."); + } + + require(self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))], "stdStorage find(StdStorage): Slot(s) not found."); + + delete self._target; + delete self._sig; + delete self._keys; + delete self._depth; + + return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; + } + + function target(StdStorage storage self, address _target) internal returns (StdStorage storage) { + self._target = _target; + return self; + } + + function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) { + self._sig = _sig; + return self; + } + + function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) { + self._sig = sigs(_sig); + return self; + } + + function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) { + self._keys.push(bytes32(uint256(uint160(who)))); + return self; + } + + function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) { + self._keys.push(bytes32(amt)); + return self; + } + function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) { + self._keys.push(key); + return self; + } + + function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) { + self._depth = _depth; + return self; + } + + function checked_write(StdStorage storage self, address who) internal { + checked_write(self, bytes32(uint256(uint160(who)))); + } + + function checked_write(StdStorage storage self, uint256 amt) internal { + checked_write(self, bytes32(amt)); + } + + function checked_write(StdStorage storage self, bool write) internal { + bytes32 t; + /// @solidity memory-safe-assembly + assembly { + t := write + } + checked_write(self, t); + } + + function checked_write( + StdStorage storage self, + bytes32 set + ) internal { + address who = self._target; + bytes4 fsig = self._sig; + uint256 field_depth = self._depth; + bytes32[] memory ins = self._keys; + + bytes memory cald = abi.encodePacked(fsig, flatten(ins)); + if (!self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { + find(self); + } + bytes32 slot = bytes32(self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]); + + bytes32 fdat; + { + (, bytes memory rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32*field_depth); + } + bytes32 curr = vm_std_store.load(who, slot); + + if (fdat != curr) { + require(false, "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported."); + } + vm_std_store.store(who, slot, set); + delete self._target; + delete self._sig; + delete self._keys; + delete self._depth; + } + + function read(StdStorage storage self) private returns (bytes memory) { + address t = self._target; + uint256 s = find(self); + return abi.encode(vm_std_store.load(t, bytes32(s))); + } + + function read_bytes32(StdStorage storage self) internal returns (bytes32) { + return abi.decode(read(self), (bytes32)); + } + + + function read_bool(StdStorage storage self) internal returns (bool) { + int256 v = read_int(self); + if (v == 0) return false; + if (v == 1) return true; + revert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); + } + + function read_address(StdStorage storage self) internal returns (address) { + return abi.decode(read(self), (address)); + } + + function read_uint(StdStorage storage self) internal returns (uint256) { + return abi.decode(read(self), (uint256)); + } + + function read_int(StdStorage storage self) internal returns (int256) { + return abi.decode(read(self), (int256)); + } + + function bytesToBytes32(bytes memory b, uint offset) public pure returns (bytes32) { + bytes32 out; + + uint256 max = b.length > 32 ? 32 : b.length; + for (uint i = 0; i < max; i++) { + out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); + } + return out; + } + + function flatten(bytes32[] memory b) private pure returns (bytes memory) + { + bytes memory result = new bytes(b.length * 32); + for (uint256 i = 0; i < b.length; i++) { + bytes32 k = b[i]; + /// @solidity memory-safe-assembly + assembly { + mstore(add(result, add(32, mul(32, i))), k) + } + } + + return result; + } + + + +} + + +/*////////////////////////////////////////////////////////////////////////// + STD-MATH +//////////////////////////////////////////////////////////////////////////*/ + +library stdMath { + int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; + + function abs(int256 a) internal pure returns (uint256) { + // Required or it will fail when `a = type(int256).min` + if (a == INT256_MIN) + return 57896044618658097711785492504343953926634992332820282019728792003956564819968; + + return uint256(a > 0 ? a : -a); + } + + function delta(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b + ? a - b + : b - a; + } + + function delta(int256 a, int256 b) internal pure returns (uint256) { + // a and b are of the same sign + // this works thanks to two's complement, the left-most bit is the sign bit + if ((a ^ b) > -1) { + return delta(abs(a), abs(b)); + } + + // a and b are of opposite signs + return abs(a) + abs(b); + } + + function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 absDelta = delta(a, b); + + return absDelta * 1e18 / b; + } + + function percentDelta(int256 a, int256 b) internal pure returns (uint256) { + uint256 absDelta = delta(a, b); + uint256 absB = abs(b); + + return absDelta * 1e18 / absB; + } +} diff --git a/lib/forge-std/src/Vm.sol b/lib/forge-std/src/Vm.sol new file mode 100644 index 0000000..fa375db --- /dev/null +++ b/lib/forge-std/src/Vm.sol @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; +pragma experimental ABIEncoderV2; + +interface Vm { + struct Log { + bytes32[] topics; + bytes data; + } + + // Sets block.timestamp (newTimestamp) + function warp(uint256) external; + // Sets block.height (newHeight) + function roll(uint256) external; + // Sets block.basefee (newBasefee) + function fee(uint256) external; + // Sets block.difficulty (newDifficulty) + function difficulty(uint256) external; + // Sets block.chainid + function chainId(uint256) external; + // Loads a storage slot from an address (who, slot) + function load(address,bytes32) external returns (bytes32); + // Stores a value to an address' storage slot, (who, slot, value) + function store(address,bytes32,bytes32) external; + // Signs data, (privateKey, digest) => (v, r, s) + function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32); + // Gets the address for a given private key, (privateKey) => (address) + function addr(uint256) external returns (address); + // Gets the nonce of an account + function getNonce(address) external returns (uint64); + // Sets the nonce of an account; must be higher than the current nonce of the account + function setNonce(address, uint64) external; + // Performs a foreign function call via the terminal, (stringInputs) => (result) + function ffi(string[] calldata) external returns (bytes memory); + // Sets environment variables, (name, value) + function setEnv(string calldata, string calldata) external; + // Reads environment variables, (name) => (value) + function envBool(string calldata) external returns (bool); + function envUint(string calldata) external returns (uint256); + function envInt(string calldata) external returns (int256); + function envAddress(string calldata) external returns (address); + function envBytes32(string calldata) external returns (bytes32); + function envString(string calldata) external returns (string memory); + function envBytes(string calldata) external returns (bytes memory); + // Reads environment variables as arrays, (name, delim) => (value[]) + function envBool(string calldata, string calldata) external returns (bool[] memory); + function envUint(string calldata, string calldata) external returns (uint256[] memory); + function envInt(string calldata, string calldata) external returns (int256[] memory); + function envAddress(string calldata, string calldata) external returns (address[] memory); + function envBytes32(string calldata, string calldata) external returns (bytes32[] memory); + function envString(string calldata, string calldata) external returns (string[] memory); + function envBytes(string calldata, string calldata) external returns (bytes[] memory); + // Sets the *next* call's msg.sender to be the input address + function prank(address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called + function startPrank(address) external; + // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input + function prank(address,address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input + function startPrank(address,address) external; + // Resets subsequent calls' msg.sender to be `address(this)` + function stopPrank() external; + // Sets an address' balance, (who, newBalance) + function deal(address, uint256) external; + // Sets an address' code, (who, newCode) + function etch(address, bytes calldata) external; + // Expects an error on next call + function expectRevert(bytes calldata) external; + function expectRevert(bytes4) external; + function expectRevert() external; + // Records all storage reads and writes + function record() external; + // Gets all accessed reads and write slot from a recording session, for a given address + function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes); + // Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). + // Call this function, then emit an event, then call a function. Internally after the call, we check if + // logs were emitted in the expected order with the expected topics and data (as specified by the booleans) + function expectEmit(bool,bool,bool,bool) external; + function expectEmit(bool,bool,bool,bool,address) external; + // Mocks a call to an address, returning specified data. + // Calldata can either be strict or a partial match, e.g. if you only + // pass a Solidity selector to the expected calldata, then the entire Solidity + // function will be mocked. + function mockCall(address,bytes calldata,bytes calldata) external; + // Mocks a call to an address with a specific msg.value, returning specified data. + // Calldata match takes precedence over msg.value in case of ambiguity. + function mockCall(address,uint256,bytes calldata,bytes calldata) external; + // Clears all mocked calls + function clearMockedCalls() external; + // Expects a call to an address with the specified calldata. + // Calldata can either be a strict or a partial match + function expectCall(address,bytes calldata) external; + // Expects a call to an address with the specified msg.value and calldata + function expectCall(address,uint256,bytes calldata) external; + // Gets the _creation_ bytecode from an artifact file. Takes in the relative path to the json file + function getCode(string calldata) external returns (bytes memory); + // Gets the _deployed_ bytecode from an artifact file. Takes in the relative path to the json file + function getDeployedCode(string calldata) external returns (bytes memory); + // Labels an address in call traces + function label(address, string calldata) external; + // If the condition is false, discard this run's fuzz inputs and generate new ones + function assume(bool) external; + // Sets block.coinbase (who) + function coinbase(address) external; + // Using the address that calls the test contract, has the next call (at this call depth only) create a transaction that can later be signed and sent onchain + function broadcast() external; + // Has the next call (at this call depth only) create a transaction with the address provided as the sender that can later be signed and sent onchain + function broadcast(address) external; + // Has the next call (at this call depth only) create a transaction with the private key provided as the sender that can later be signed and sent onchain + function broadcast(uint256) external; + // Using the address that calls the test contract, has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain + function startBroadcast() external; + // Has all subsequent calls (at this call depth only) create transactions with the address provided that can later be signed and sent onchain + function startBroadcast(address) external; + // Has all subsequent calls (at this call depth only) create transactions with the private key provided that can later be signed and sent onchain + function startBroadcast(uint256) external; + // Stops collecting onchain transactions + function stopBroadcast() external; + + // Reads the entire content of file to string, (path) => (data) + function readFile(string calldata) external returns (string memory); + // Reads the entire content of file as binary. Path is relative to the project root. (path) => (data) + function readFileBinary(string calldata) external returns (bytes memory); + // Get the path of the current project root + function projectRoot() external returns (string memory); + // Reads next line of file to string, (path) => (line) + function readLine(string calldata) external returns (string memory); + // Writes data to file, creating a file if it does not exist, and entirely replacing its contents if it does. + // (path, data) => () + function writeFile(string calldata, string calldata) external; + // Writes binary data to a file, creating a file if it does not exist, and entirely replacing its contents if it does. + // Path is relative to the project root. (path, data) => () + function writeFileBinary(string calldata, bytes calldata) external; + // Writes line to file, creating a file if it does not exist. + // (path, data) => () + function writeLine(string calldata, string calldata) external; + // Closes file for reading, resetting the offset and allowing to read it from beginning with readLine. + // (path) => () + function closeFile(string calldata) external; + // Removes file. This cheatcode will revert in the following situations, but is not limited to just these cases: + // - Path points to a directory. + // - The file doesn't exist. + // - The user lacks permissions to remove the file. + // (path) => () + function removeFile(string calldata) external; + + // Convert values to a string, (value) => (stringified value) + function toString(address) external returns(string memory); + function toString(bytes calldata) external returns(string memory); + function toString(bytes32) external returns(string memory); + function toString(bool) external returns(string memory); + function toString(uint256) external returns(string memory); + function toString(int256) external returns(string memory); + + // Convert values from a string, (string) => (parsed value) + function parseBytes(string calldata) external returns (bytes memory); + function parseAddress(string calldata) external returns (address); + function parseUint(string calldata) external returns (uint256); + function parseInt(string calldata) external returns (int256); + function parseBytes32(string calldata) external returns (bytes32); + function parseBool(string calldata) external returns (bool); + + // Record all the transaction logs + function recordLogs() external; + // Gets all the recorded logs, () => (logs) + function getRecordedLogs() external returns (Log[] memory); + // Snapshot the current state of the evm. + // Returns the id of the snapshot that was created. + // To revert a snapshot use `revertTo` + function snapshot() external returns(uint256); + // Revert the state of the evm to a previous snapshot + // Takes the snapshot id to revert to. + // This deletes the snapshot and all snapshots taken after the given snapshot id. + function revertTo(uint256) external returns(bool); + + // Creates a new fork with the given endpoint and block and returns the identifier of the fork + function createFork(string calldata,uint256) external returns(uint256); + // Creates a new fork with the given endpoint and the _latest_ block and returns the identifier of the fork + function createFork(string calldata) external returns(uint256); + // Creates a new fork with the given endpoint and at the block the given transaction was mined in, and replays all transaction mined in the block before the transaction + function createFork(string calldata, bytes32) external returns (uint256); + // Creates _and_ also selects a new fork with the given endpoint and block and returns the identifier of the fork + function createSelectFork(string calldata,uint256) external returns(uint256); + // Creates _and_ also selects new fork with the given endpoint and at the block the given transaction was mined in, and replays all transaction mined in the block before the transaction + function createSelectFork(string calldata, bytes32) external returns (uint256); + // Creates _and_ also selects a new fork with the given endpoint and the latest block and returns the identifier of the fork + function createSelectFork(string calldata) external returns(uint256); + // Takes a fork identifier created by `createFork` and sets the corresponding forked state as active. + function selectFork(uint256) external; + /// Returns the currently active fork + /// Reverts if no fork is currently active + function activeFork() external returns(uint256); + // Updates the currently active fork to given block number + // This is similar to `roll` but for the currently active fork + function rollFork(uint256) external; + // Updates the currently active fork to given transaction + // this will `rollFork` with the number of the block the transaction was mined in and replays all transaction mined before it in the block + function rollFork(bytes32) external; + // Updates the given fork to given block number + function rollFork(uint256 forkId, uint256 blockNumber) external; + // Updates the given fork to block number of the given transaction and replays all transaction mined before it in the block + function rollFork(uint256 forkId, bytes32 transaction) external; + + // Marks that the account(s) should use persistent storage across fork swaps in a multifork setup + // Meaning, changes made to the state of this account will be kept when switching forks + function makePersistent(address) external; + function makePersistent(address, address) external; + function makePersistent(address, address, address) external; + function makePersistent(address[] calldata) external; + // Revokes persistent status from the address, previously added via `makePersistent` + function revokePersistent(address) external; + function revokePersistent(address[] calldata) external; + // Returns true if the account is marked as persistent + function isPersistent(address) external returns (bool); + + // In forking mode, explicitly grant the given address cheatcode access + function allowCheatcodes(address) external; + + // Fetches the given transaction from the active fork and executes it on the current state + function transact(bytes32 txHash) external; + // Fetches the given transaction from the given fork and executes it on the current state + function transact(uint256 forkId, bytes32 txHash) external; + + // Returns the RPC url for the given alias + function rpcUrl(string calldata) external returns(string memory); + // Returns all rpc urls and their aliases `[alias, url][]` + function rpcUrls() external returns(string[2][] memory); + + // Derive a private key from a provided mnenomic string (or mnenomic file path) at the derivation path m/44'/60'/0'/0/{index} + function deriveKey(string calldata, uint32) external returns (uint256); + // Derive a private key from a provided mnenomic string (or mnenomic file path) at the derivation path {path}{index} + function deriveKey(string calldata, string calldata, uint32) external returns (uint256); + // Adds a private key to the local forge wallet and returns the address + function rememberKey(uint256) external returns (address); + + // parseJson + + // Given a string of JSON, return the ABI-encoded value of provided key + // (stringified json, key) => (ABI-encoded data) + // Read the note below! + function parseJson(string calldata, string calldata) external returns(bytes memory); + + // Given a string of JSON, return it as ABI-encoded, (stringified json, key) => (ABI-encoded data) + // Read the note below! + function parseJson(string calldata) external returns(bytes memory); + + // Note: + // ---- + // In case the returned value is a JSON object, it's encoded as a ABI-encoded tuple. As JSON objects + // don't have the notion of ordered, but tuples do, they JSON object is encoded with it's fields ordered in + // ALPHABETICAL ordser. That means that in order to succesfully decode the tuple, we need to define a tuple that + // encodes the fields in the same order, which is alphabetical. In the case of Solidity structs, they are encoded + // as tuples, with the attributes in the order in which they are defined. + // For example: json = { 'a': 1, 'b': 0xa4tb......3xs} + // a: uint256 + // b: address + // To decode that json, we need to define a struct or a tuple as follows: + // struct json = { uint256 a; address b; } + // If we defined a json struct with the opposite order, meaning placing the address b first, it would try to + // decode the tuple in that order, and thus fail. + +} diff --git a/lib/forge-std/src/console.sol b/lib/forge-std/src/console.sol new file mode 100644 index 0000000..ad57e53 --- /dev/null +++ b/lib/forge-std/src/console.sol @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +library console { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + /// @solidity memory-safe-assembly + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); + } + + function logUint(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); + } + + function log(uint p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); + } + + function log(uint p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); + } + + function log(uint p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); + } + + function log(string memory p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); + } + + function log(uint p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); + } + + function log(uint p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); + } + + function log(uint p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); + } + + function log(uint p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); + } + + function log(uint p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); + } + + function log(uint p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); + } + + function log(uint p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); + } + + function log(uint p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); + } + + function log(uint p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); + } + + function log(uint p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); + } + + function log(uint p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); + } + + function log(bool p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); + } + + function log(bool p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); + } + + function log(bool p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); + } + + function log(address p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); + } + + function log(address p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); + } + + function log(address p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} \ No newline at end of file diff --git a/lib/forge-std/src/console2.sol b/lib/forge-std/src/console2.sol new file mode 100644 index 0000000..2edfda5 --- /dev/null +++ b/lib/forge-std/src/console2.sol @@ -0,0 +1,1538 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +// The orignal console.sol uses `int` and `uint` for computing function selectors, but it should +// use `int256` and `uint256`. This modified version fixes that. This version is recommended +// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in +// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`. +// Reference: https://github.com/NomicFoundation/hardhat/issues/2178 + +library console2 { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); + } + + function logUint(uint256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint256 p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); + } + + function log(uint256 p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); + } + + function log(uint256 p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); + } + + function log(uint256 p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); + } + + function log(string memory p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint256 p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} \ No newline at end of file diff --git a/lib/forge-std/src/test/Script.t.sol b/lib/forge-std/src/test/Script.t.sol new file mode 100644 index 0000000..b26db7f --- /dev/null +++ b/lib/forge-std/src/test/Script.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract ScriptTest is Test +{ + function testGenerateCorrectAddress() external { + address creation = computeCreateAddress(0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9, 14); + assertEq(creation, 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45); + } + + function testDeriveRememberKey() external { + string memory mnemonic = "test test test test test test test test test test test junk"; + + (address deployer, uint256 privateKey) = deriveRememberKey(mnemonic, 0); + assertEq(deployer, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + assertEq(privateKey, 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80); + } +} \ No newline at end of file diff --git a/lib/forge-std/src/test/StdAssertions.t.sol b/lib/forge-std/src/test/StdAssertions.t.sol new file mode 100644 index 0000000..3f26f76 --- /dev/null +++ b/lib/forge-std/src/test/StdAssertions.t.sol @@ -0,0 +1,602 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract StdAssertionsTest is Test +{ + string constant CUSTOM_ERROR = "guh!"; + + bool constant EXPECT_PASS = false; + bool constant EXPECT_FAIL = true; + + TestTest t = new TestTest(); + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertions() public { + assertEqUint(uint32(1), uint32(1)); + assertEqUint(uint64(1), uint64(1)); + assertEqUint(uint96(1), uint96(1)); + assertEqUint(uint128(1), uint128(1)); + } + + /*////////////////////////////////////////////////////////////////////////// + FAIL(STRING) + //////////////////////////////////////////////////////////////////////////*/ + + function testShouldFail() external { + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._fail(CUSTOM_ERROR); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_FALSE + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertFalse_Pass() external { + t._assertFalse(false, EXPECT_PASS); + } + + function testAssertFalse_Fail() external { + vm.expectEmit(false, false, false, true); + emit log("Error: Assertion Failed"); + t._assertFalse(true, EXPECT_FAIL); + } + + function testAssertFalse_Err_Pass() external { + t._assertFalse(false, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertFalse_Err_Fail() external { + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertFalse(true, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(BOOL) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_Bool_Pass(bool a) external { + t._assertEq(a, a, EXPECT_PASS); + } + + function testAssertEq_Bool_Fail(bool a, bool b) external { + vm.assume(a != b); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [bool]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_BoolErr_Pass(bool a) external { + t._assertEq(a, a, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertEq_BoolErr_Fail(bool a, bool b) external { + vm.assume(a != b); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(BYTES) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_Bytes_Pass(bytes calldata a) external { + t._assertEq(a, a, EXPECT_PASS); + } + + function testAssertEq_Bytes_Fail(bytes calldata a, bytes calldata b) external { + vm.assume(keccak256(a) != keccak256(b)); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [bytes]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_BytesErr_Pass(bytes calldata a) external { + t._assertEq(a, a, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertEq_BytesErr_Fail(bytes calldata a, bytes calldata b) external { + vm.assume(keccak256(a) != keccak256(b)); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(ARRAY) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_UintArr_Pass(uint256 e0, uint256 e1, uint256 e2) public { + uint256[] memory a = new uint256[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + uint256[] memory b = new uint256[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_IntArr_Pass(int256 e0, int256 e1, int256 e2) public { + int256[] memory a = new int256[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + int256[] memory b = new int256[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_AddressArr_Pass(address e0, address e1, address e2) public { + address[] memory a = new address[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + address[] memory b = new address[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_UintArr_FailEl(uint256 e1) public { + vm.assume(e1 != 0); + uint256[] memory a = new uint256[](3); + uint256[] memory b = new uint256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_IntArr_FailEl(int256 e1) public { + vm.assume(e1 != 0); + int256[] memory a = new int256[](3); + int256[] memory b = new int256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + + function testAssertEq_AddressArr_FailEl(address e1) public { + vm.assume(e1 != address(0)); + address[] memory a = new address[](3); + address[] memory b = new address[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_UintArrErr_FailEl(uint256 e1) public { + vm.assume(e1 != 0); + uint256[] memory a = new uint256[](3); + uint256[] memory b = new uint256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_IntArrErr_FailEl(int256 e1) public { + vm.assume(e1 != 0); + int256[] memory a = new int256[](3); + int256[] memory b = new int256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + + function testAssertEq_AddressArrErr_FailEl(address e1) public { + vm.assume(e1 != address(0)); + address[] memory a = new address[](3); + address[] memory b = new address[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_UintArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + uint256[] memory a = new uint256[](lenA); + uint256[] memory b = new uint256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_IntArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + int256[] memory a = new int256[](lenA); + int256[] memory b = new int256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_AddressArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + address[] memory a = new address[](lenA); + address[] memory b = new address[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_UintArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + uint256[] memory a = new uint256[](lenA); + uint256[] memory b = new uint256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_IntArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + int256[] memory a = new int256[](lenA); + int256[] memory b = new int256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_AddressArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + address[] memory a = new address[](lenA); + address[] memory b = new address[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_ABS(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqAbs_Uint_Pass(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_PASS); + } + + function testAssertApproxEqAbs_Uint_Fail(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [uint]"); + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_FAIL); + } + + function testAssertApproxEqAbs_UintErr_Pass(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqAbs_UintErr_Fail(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_ABS(INT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqAbs_Int_Pass(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_PASS); + } + + function testAssertApproxEqAbs_Int_Fail(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [int]"); + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_FAIL); + } + + function testAssertApproxEqAbs_IntErr_Pass(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqAbs_IntErr_Fail(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_REL(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqRel_Uint_Pass(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_PASS); + } + + function testAssertApproxEqRel_Uint_Fail(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [uint]"); + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_FAIL); + } + + function testAssertApproxEqRel_UintErr_Pass(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqRel_UintErr_Fail(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_REL(INT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqRel_Int_Pass(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_PASS); + } + + function testAssertApproxEqRel_Int_Fail(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [int]"); + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_FAIL); + } + + function testAssertApproxEqRel_IntErr_Pass(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqRel_IntErr_Fail(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_FAIL); + } +} + + +contract TestTest is Test +{ + modifier expectFailure(bool expectFail) { + bool preState = vm.load(HEVM_ADDRESS, bytes32("failed")) != bytes32(0x00); + _; + bool postState = vm.load(HEVM_ADDRESS, bytes32("failed")) != bytes32(0x00); + + if (preState == true) { + return; + } + + if (expectFail) { + require(postState == true, "expected failure not triggered"); + + // unwind the expected failure + vm.store(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x00))); + } else { + require(postState == false, "unexpected failure was triggered"); + } + } + + function _fail(string memory err) external expectFailure(true) { + fail(err); + } + + function _assertFalse(bool data, bool expectFail) external expectFailure(expectFail) { + assertFalse(data); + } + + function _assertFalse(bool data, string memory err, bool expectFail) external expectFailure(expectFail) { + assertFalse(data, err); + } + + function _assertEq(bool a, bool b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(bool a, bool b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(bytes memory a, bytes memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(bytes memory a, + bytes memory b, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(uint256[] memory a, uint256[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(int256[] memory a, int256[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(address[] memory a, address[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(uint256[] memory a, uint256[] memory b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(int256[] memory a, int256[] memory b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(address[] memory a, address[] memory b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + + function _assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta); + } + + function _assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta, err); + } + + function _assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta); + } + + function _assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta, err); + } + + function _assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta); + } + + function _assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta, err); + } + + function _assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta); + } + + function _assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta, err); + } +} diff --git a/lib/forge-std/src/test/StdCheats.t.sol b/lib/forge-std/src/test/StdCheats.t.sol new file mode 100644 index 0000000..05e240a --- /dev/null +++ b/lib/forge-std/src/test/StdCheats.t.sol @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; +import "../StdJson.sol"; + +contract StdCheatsTest is Test { + Bar test; + + using stdJson for string; + + function setUp() public { + test = new Bar(); + } + + function testSkip() public { + vm.warp(100); + skip(25); + assertEq(block.timestamp, 125); + } + + function testRewind() public { + vm.warp(100); + rewind(25); + assertEq(block.timestamp, 75); + } + + function testHoax() public { + hoax(address(1337)); + test.bar{value: 100}(address(1337)); + } + + function testHoaxOrigin() public { + hoax(address(1337), address(1337)); + test.origin{value: 100}(address(1337)); + } + + function testHoaxDifferentAddresses() public { + hoax(address(1337), address(7331)); + test.origin{value: 100}(address(1337), address(7331)); + } + + function testStartHoax() public { + startHoax(address(1337)); + test.bar{value: 100}(address(1337)); + test.bar{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } + + function testStartHoaxOrigin() public { + startHoax(address(1337), address(1337)); + test.origin{value: 100}(address(1337)); + test.origin{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } + + function testChangePrank() public { + vm.startPrank(address(1337)); + test.bar(address(1337)); + changePrank(address(0xdead)); + test.bar(address(0xdead)); + changePrank(address(1337)); + test.bar(address(1337)); + vm.stopPrank(); + } + + function testMakeAddrEquivalence() public { + (address addr, ) = makeAddrAndKey("1337"); + assertEq(makeAddr("1337"), addr); + } + + function testMakeAddrSigning() public { + (address addr, uint256 key) = makeAddrAndKey("1337"); + bytes32 hash = keccak256("some_message"); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(key, hash); + assertEq(ecrecover(hash, v, r, s), addr); + } + + function testDeal() public { + deal(address(this), 1 ether); + assertEq(address(this).balance, 1 ether); + } + + function testDealToken() public { + Bar barToken = new Bar(); + address bar = address(barToken); + deal(bar, address(this), 10000e18); + assertEq(barToken.balanceOf(address(this)), 10000e18); + } + + function testDealTokenAdjustTS() public { + Bar barToken = new Bar(); + address bar = address(barToken); + deal(bar, address(this), 10000e18, true); + assertEq(barToken.balanceOf(address(this)), 10000e18); + assertEq(barToken.totalSupply(), 20000e18); + deal(bar, address(this), 0, true); + assertEq(barToken.balanceOf(address(this)), 0); + assertEq(barToken.totalSupply(), 10000e18); + } + + function testBound() public { + assertEq(bound(5, 0, 4), 0); + assertEq(bound(0, 69, 69), 69); + assertEq(bound(0, 68, 69), 68); + assertEq(bound(10, 150, 190), 160); + assertEq(bound(300, 2800, 3200), 3100); + assertEq(bound(9999, 1337, 6666), 6006); + } + + function testCannotBoundMaxLessThanMin() public { + vm.expectRevert(bytes("Test bound(uint256,uint256,uint256): Max is less than min.")); + bound(5, 100, 10); + } + + function testBound( + uint256 num, + uint256 min, + uint256 max + ) public { + if (min > max) (min, max) = (max, min); + + uint256 bounded = bound(num, min, max); + + assertGe(bounded, min); + assertLe(bounded, max); + } + + function testBoundUint256Max() public { + assertEq(bound(0, type(uint256).max - 1, type(uint256).max), type(uint256).max - 1); + assertEq(bound(1, type(uint256).max - 1, type(uint256).max), type(uint256).max); + } + + function testCannotBoundMaxLessThanMin( + uint256 num, + uint256 min, + uint256 max + ) public { + vm.assume(min > max); + vm.expectRevert(bytes("Test bound(uint256,uint256,uint256): Max is less than min.")); + bound(num, min, max); + } + + function testDeployCode() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", bytes("")); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + } + + function testDeployCodeNoArgs() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest"); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + } + + // We need that payable constructor in order to send ETH on construction + constructor() payable {} + + function testDeployCodeVal() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", bytes(""), 1 ether); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + assertEq(deployed.balance, 1 ether); + } + + function testDeployCodeValNoArgs() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", 1 ether); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + assertEq(deployed.balance, 1 ether); + } + + // We need this so we can call "this.deployCode" rather than "deployCode" directly + function deployCodeHelper(string memory what) external { + deployCode(what); + } + + function testDeployCodeFail() public { + vm.expectRevert(bytes("Test deployCode(string): Deployment failed.")); + this.deployCodeHelper("StdCheats.t.sol:RevertingContract"); + } + + function getCode(address who) internal view returns (bytes memory o_code) { + /// @solidity memory-safe-assembly + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(who) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(who, add(o_code, 0x20), 0, size) + } + } + + function testBytesToUint() public { + assertEq(3, bytesToUint(hex'03')); + assertEq(2, bytesToUint(hex'02')); + assertEq(255, bytesToUint(hex'ff')); + assertEq(29625, bytesToUint(hex'73b9')); + } + + function testParseJsonTxDetail() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + string memory json = vm.readFile(path); + bytes memory transactionDetails = json.parseRaw(".transactions[0].tx"); + RawTx1559Detail memory rawTxDetail = abi.decode(transactionDetails, (RawTx1559Detail)); + Tx1559Detail memory txDetail = rawToConvertedEIP1559Detail(rawTxDetail); + assertEq(txDetail.from, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + assertEq(txDetail.to, 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512); + assertEq(txDetail.data, hex'23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004'); + assertEq(txDetail.nonce, 3); + assertEq(txDetail.txType, 2); + assertEq(txDetail.gas, 29625); + assertEq(txDetail.value, 0); + } + + function testReadEIP1559Transaction() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + uint256 index = 0; + Tx1559 memory transaction = readTx1559(path, index); + } + + function testReadEIP1559Transactions() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + Tx1559[] memory transactions = readTx1559s(path); + } + + function testReadReceipt() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + uint index = 5; + Receipt memory receipt = readReceipt(path, index); + assertEq(receipt.logsBloom, + hex"00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100"); + } + + function testReadReceipts() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + Receipt[] memory receipts = readReceipts(path); + } + +} + +contract Bar { + constructor() { + /// `DEAL` STDCHEAT + totalSupply = 10000e18; + balanceOf[address(this)] = totalSupply; + } + + /// `HOAX` STDCHEATS + function bar(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + } + function origin(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + require(tx.origin == expectedSender, "!prank"); + } + function origin(address expectedSender, address expectedOrigin) public payable { + require(msg.sender == expectedSender, "!prank"); + require(tx.origin == expectedOrigin, "!prank"); + } + + /// `DEAL` STDCHEAT + mapping (address => uint256) public balanceOf; + uint256 public totalSupply; +} + +contract RevertingContract { + constructor() { + revert(); + } +} + diff --git a/lib/forge-std/src/test/StdError.t.sol b/lib/forge-std/src/test/StdError.t.sol new file mode 100644 index 0000000..0d6601e --- /dev/null +++ b/lib/forge-std/src/test/StdError.t.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.10 <0.9.0; + +import "../Test.sol"; + +contract StdErrorsTest is Test { + ErrorsTest test; + + function setUp() public { + test = new ErrorsTest(); + } + + function testExpectAssertion() public { + vm.expectRevert(stdError.assertionError); + test.assertionError(); + } + + function testExpectArithmetic() public { + vm.expectRevert(stdError.arithmeticError); + test.arithmeticError(10); + } + + function testExpectDiv() public { + vm.expectRevert(stdError.divisionError); + test.divError(0); + } + + function testExpectMod() public { + vm.expectRevert(stdError.divisionError); + test.modError(0); + } + + function testExpectEnum() public { + vm.expectRevert(stdError.enumConversionError); + test.enumConversion(1); + } + + function testExpectEncodeStg() public { + vm.expectRevert(stdError.encodeStorageError); + test.encodeStgError(); + } + + function testExpectPop() public { + vm.expectRevert(stdError.popError); + test.pop(); + } + + function testExpectOOB() public { + vm.expectRevert(stdError.indexOOBError); + test.indexOOBError(1); + } + + function testExpectMem() public { + vm.expectRevert(stdError.memOverflowError); + test.mem(); + } + + function testExpectIntern() public { + vm.expectRevert(stdError.zeroVarError); + test.intern(); + } + + function testExpectLowLvl() public { + vm.expectRevert(stdError.lowLevelError); + test.someArr(0); + } +} + +contract ErrorsTest { + enum T { + T1 + } + + uint256[] public someArr; + bytes someBytes; + + function assertionError() public pure { + assert(false); + } + + function arithmeticError(uint256 a) public pure { + a -= 100; + } + + function divError(uint256 a) public pure { + 100 / a; + } + + function modError(uint256 a) public pure { + 100 % a; + } + + function enumConversion(uint256 a) public pure { + T(a); + } + + function encodeStgError() public { + /// @solidity memory-safe-assembly + assembly { + sstore(someBytes.slot, 1) + } + keccak256(someBytes); + } + + function pop() public { + someArr.pop(); + } + + function indexOOBError(uint256 a) public pure { + uint256[] memory t = new uint256[](0); + t[a]; + } + + function mem() public pure { + uint256 l = 2**256 / 32; + new uint256[](l); + } + + function intern() public returns (uint256) { + function(uint256) internal returns (uint256) x; + x(2); + return 7; + } +} diff --git a/lib/forge-std/src/test/StdMath.t.sol b/lib/forge-std/src/test/StdMath.t.sol new file mode 100644 index 0000000..9d09b81 --- /dev/null +++ b/lib/forge-std/src/test/StdMath.t.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +import "../Test.sol"; + +contract StdMathTest is Test +{ + function testGetAbs() external { + assertEq(stdMath.abs(-50), 50); + assertEq(stdMath.abs(50), 50); + assertEq(stdMath.abs(-1337), 1337); + assertEq(stdMath.abs(0), 0); + + assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1); + assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1)); + } + + function testGetAbs_Fuzz(int256 a) external { + uint256 manualAbs = getAbs(a); + + uint256 abs = stdMath.abs(a); + + assertEq(abs, manualAbs); + } + + function testGetDelta_Uint() external { + assertEq(stdMath.delta(uint256(0), uint256(0)), 0); + assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337); + assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max); + assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max); + assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max); + + assertEq(stdMath.delta(0, uint256(0)), 0); + assertEq(stdMath.delta(1337, uint256(0)), 1337); + assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max); + assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max); + assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max); + + assertEq(stdMath.delta(1337, uint256(1337)), 0); + assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0); + assertEq(stdMath.delta(5000, uint256(1250)), 3750); + } + + function testGetDelta_Uint_Fuzz(uint256 a, uint256 b) external { + uint256 manualDelta; + if (a > b) { + manualDelta = a - b; + } else { + manualDelta = b - a; + } + + uint256 delta = stdMath.delta(a, b); + + assertEq(delta, manualDelta); + } + + function testGetDelta_Int() external { + assertEq(stdMath.delta(int256(0), int256(0)), 0); + assertEq(stdMath.delta(int256(0), int256(1337)), 1337); + assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1); + assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1); + assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1); + + assertEq(stdMath.delta(0, int256(0)), 0); + assertEq(stdMath.delta(1337, int256(0)), 1337); + assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1); + assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1); + assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1); + + assertEq(stdMath.delta(-0, int256(0)), 0); + assertEq(stdMath.delta(-1337, int256(0)), 1337); + assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1); + assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1); + assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1); + + assertEq(stdMath.delta(int256(0), -0), 0); + assertEq(stdMath.delta(int256(0), -1337), 1337); + assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1); + assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1); + assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1); + + assertEq(stdMath.delta(1337, int256(1337)), 0); + assertEq(stdMath.delta(type(int256).max, type(int256).max), 0); + assertEq(stdMath.delta(type(int256).min, type(int256).min), 0); + assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max); + assertEq(stdMath.delta(5000, int256(1250)), 3750); + } + + function testGetDelta_Int_Fuzz(int256 a, int256 b) external { + uint256 absA = getAbs(a); + uint256 absB = getAbs(b); + uint256 absDelta = absA > absB + ? absA - absB + : absB - absA; + + uint256 manualDelta; + if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { + manualDelta = absDelta; + } + // (a < 0 && b >= 0) || (a >= 0 && b < 0) + else { + manualDelta = absA + absB; + } + + uint256 delta = stdMath.delta(a, b); + + assertEq(delta, manualDelta); + } + + function testGetPercentDelta_Uint() external { + assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18); + + assertEq(stdMath.percentDelta(1337, uint256(1337)), 0); + assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0); + assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18); + assertEq(stdMath.percentDelta(2500, uint256(2500)), 0); + assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18); + assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18); + + vm.expectRevert(stdError.divisionError); + stdMath.percentDelta(uint256(1), 0); + } + + function testGetPercentDelta_Uint_Fuzz(uint192 a, uint192 b) external { + vm.assume(b != 0); + uint256 manualDelta; + if (a > b) { + manualDelta = a - b; + } else { + manualDelta = b - a; + } + + uint256 manualPercentDelta = manualDelta * 1e18 / b; + uint256 percentDelta = stdMath.percentDelta(a, b); + + assertEq(percentDelta, manualPercentDelta); + } + + function testGetPercentDelta_Int() external { + assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18); + assertEq(stdMath.percentDelta(int256(0), -1337), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18); + + assertEq(stdMath.percentDelta(1337, int256(1337)), 0); + assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0); + assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0); + + assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down + assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down + assertEq(stdMath.percentDelta(0, int256(2500)), 1e18); + assertEq(stdMath.percentDelta(2500, int256(2500)), 0); + assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18); + assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18); + + vm.expectRevert(stdError.divisionError); + stdMath.percentDelta(int256(1), 0); + } + + function testGetPercentDelta_Int_Fuzz(int192 a, int192 b) external { + vm.assume(b != 0); + uint256 absA = getAbs(a); + uint256 absB = getAbs(b); + uint256 absDelta = absA > absB + ? absA - absB + : absB - absA; + + uint256 manualDelta; + if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { + manualDelta = absDelta; + } + // (a < 0 && b >= 0) || (a >= 0 && b < 0) + else { + manualDelta = absA + absB; + } + + uint256 manualPercentDelta = manualDelta * 1e18 / absB; + uint256 percentDelta = stdMath.percentDelta(a, b); + + assertEq(percentDelta, manualPercentDelta); + } + + /*////////////////////////////////////////////////////////////////////////// + HELPERS + //////////////////////////////////////////////////////////////////////////*/ + + function getAbs(int256 a) private pure returns (uint256) { + if (a < 0) + return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a); + + return uint256(a); + } +} diff --git a/lib/forge-std/src/test/StdStorage.t.sol b/lib/forge-std/src/test/StdStorage.t.sol new file mode 100644 index 0000000..6e238d0 --- /dev/null +++ b/lib/forge-std/src/test/StdStorage.t.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract StdStorageTest is Test { + using stdStorage for StdStorage; + + StorageTest test; + + function setUp() public { + test = new StorageTest(); + } + + function testStorageHidden() public { + assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find()); + } + + function testStorageObvious() public { + assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find()); + } + + function testStorageCheckedWriteHidden() public { + stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100); + assertEq(uint256(test.hidden()), 100); + } + + function testStorageCheckedWriteObvious() public { + stdstore.target(address(test)).sig(test.exists.selector).checked_write(100); + assertEq(test.exists(), 100); + } + + function testStorageMapStructA() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(0) + .find(); + assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot); + } + + function testStorageMapStructB() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(1) + .find(); + assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot); + } + + function testStorageDeepMap() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.deep_map.selector) + .with_key(address(this)) + .with_key(address(this)) + .find(); + assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint(5)))))), slot); + } + + function testStorageCheckedWriteDeepMap() public { + stdstore + .target(address(test)) + .sig(test.deep_map.selector) + .with_key(address(this)) + .with_key(address(this)) + .checked_write(100); + assertEq(100, test.deep_map(address(this), address(this))); + } + + function testStorageDeepMapStructA() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(0) + .find(); + assertEq(bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint(6)))))) + 0), bytes32(slot)); + } + + function testStorageDeepMapStructB() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(1) + .find(); + assertEq(bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint(6)))))) + 1), bytes32(slot)); + } + + function testStorageCheckedWriteDeepMapStructA() public { + stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(0) + .checked_write(100); + (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); + assertEq(100, a); + assertEq(0, b); + } + + function testStorageCheckedWriteDeepMapStructB() public { + stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(1) + .checked_write(100); + (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); + assertEq(0, a); + assertEq(100, b); + } + + function testStorageCheckedWriteMapStructA() public { + stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(0) + .checked_write(100); + (uint256 a, uint256 b) = test.map_struct(address(this)); + assertEq(a, 100); + assertEq(b, 0); + } + + function testStorageCheckedWriteMapStructB() public { + stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(1) + .checked_write(100); + (uint256 a, uint256 b) = test.map_struct(address(this)); + assertEq(a, 0); + assertEq(b, 100); + } + + function testStorageStructA() public { + uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find(); + assertEq(uint256(7), slot); + } + + function testStorageStructB() public { + uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find(); + assertEq(uint256(7) + 1, slot); + } + + function testStorageCheckedWriteStructA() public { + stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100); + (uint256 a, uint256 b) = test.basic(); + assertEq(a, 100); + assertEq(b, 1337); + } + + function testStorageCheckedWriteStructB() public { + stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100); + (uint256 a, uint256 b) = test.basic(); + assertEq(a, 1337); + assertEq(b, 100); + } + + function testStorageMapAddrFound() public { + uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find(); + assertEq(uint256(keccak256(abi.encode(address(this), uint(1)))), slot); + } + + function testStorageMapUintFound() public { + uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find(); + assertEq(uint256(keccak256(abi.encode(100, uint(2)))), slot); + } + + function testStorageCheckedWriteMapUint() public { + stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100); + assertEq(100, test.map_uint(100)); + } + + function testStorageCheckedWriteMapAddr() public { + stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100); + assertEq(100, test.map_addr(address(this))); + } + + function testStorageCheckedWriteMapBool() public { + stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true); + assertTrue(test.map_bool(address(this))); + } + + function testFailStorageCheckedWriteMapPacked() public { + // expect PackedSlot error but not external call so cant expectRevert + stdstore.target(address(test)).sig(test.read_struct_lower.selector).with_key(address(uint160(1337))).checked_write(100); + } + + function testStorageCheckedWriteMapPackedSuccess() public { + uint256 full = test.map_packed(address(1337)); + // keep upper 128, set lower 128 to 1337 + full = (full & (uint256((1 << 128) - 1) << 128)) | 1337; + stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337))).checked_write(full); + assertEq(1337, test.read_struct_lower(address(1337))); + } + + function testFailStorageConst() public { + // vm.expectRevert(abi.encodeWithSignature("NotStorage(bytes4)", bytes4(keccak256("const()")))); + stdstore.target(address(test)).sig("const()").find(); + } + + function testFailStorageNativePack() public { + stdstore.target(address(test)).sig(test.tA.selector).find(); + stdstore.target(address(test)).sig(test.tB.selector).find(); + + // these both would fail + stdstore.target(address(test)).sig(test.tC.selector).find(); + stdstore.target(address(test)).sig(test.tD.selector).find(); + } + + function testStorageReadBytes32() public { + bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32(); + assertEq(val, hex"1337"); + } + + function testStorageReadBool_False() public { + bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool(); + assertEq(val, false); + } + + function testStorageReadBool_True() public { + bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool(); + assertEq(val, true); + } + + function testStorageReadBool_Revert() public { + vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); + this.readNonBoolValue(); + } + + function readNonBoolValue() public { + stdstore.target(address(test)).sig(test.tE.selector).read_bool(); + } + + function testStorageReadAddress() public { + address val = stdstore.target(address(test)).sig(test.tF.selector).read_address(); + assertEq(val, address(1337)); + } + + function testStorageReadUint() public { + uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint(); + assertEq(val, 1); + } + + function testStorageReadInt() public { + int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int(); + assertEq(val, type(int256).min); + } +} + +contract StorageTest { + uint256 public exists = 1; + mapping(address => uint256) public map_addr; + mapping(uint256 => uint256) public map_uint; + mapping(address => uint256) public map_packed; + mapping(address => UnpackedStruct) public map_struct; + mapping(address => mapping(address => uint256)) public deep_map; + mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; + UnpackedStruct public basic; + + uint248 public tA; + bool public tB; + + + bool public tC = false; + uint248 public tD = 1; + + + struct UnpackedStruct { + uint256 a; + uint256 b; + } + + mapping(address => bool) public map_bool; + + bytes32 public tE = hex"1337"; + address public tF = address(1337); + int256 public tG = type(int256).min; + bool public tH = true; + + constructor() { + basic = UnpackedStruct({ + a: 1337, + b: 1337 + }); + + uint256 two = (1<<128) | 1; + map_packed[msg.sender] = two; + map_packed[address(bytes20(uint160(1337)))] = 1<<128; + } + + function read_struct_upper(address who) public view returns (uint256) { + return map_packed[who] >> 128; + } + + function read_struct_lower(address who) public view returns (uint256) { + return map_packed[who] & ((1 << 128) - 1); + } + + function hidden() public view returns (bytes32 t) { + bytes32 slot = keccak256("my.random.var"); + /// @solidity memory-safe-assembly + assembly { + t := sload(slot) + } + } + + function const() public pure returns (bytes32 t) { + t = bytes32(hex"1337"); + } +} diff --git a/lib/forge-std/src/test/fixtures/broadcast.log.json b/lib/forge-std/src/test/fixtures/broadcast.log.json new file mode 100644 index 0000000..0a0200b --- /dev/null +++ b/lib/forge-std/src/test/fixtures/broadcast.log.json @@ -0,0 +1,187 @@ +{ + "transactions": [ + { + "hash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "function": "multiple_arguments(uint256,address,uint256[]):(uint256)", + "arguments": ["1", "0000000000000000000000000000000000001337", "[3,4]"], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "gas": "0x73b9", + "value": "0x0", + "data": "0x23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004", + "nonce": "0x3", + "accessList": [] + } + }, + { + "hash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "function": "inc():(uint256)", + "arguments": [], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "gas": "0xdcb2", + "value": "0x0", + "data": "0x371303c0", + "nonce": "0x4", + "accessList": [] + } + }, + { + "hash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "function": "t(uint256):(uint256)", + "arguments": ["1"], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "gas": "0x8599", + "value": "0x0", + "data": "0xafe29f710000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x5", + "accessList": [] + } + } + ], + "receipts": [ + { + "transactionHash": "0x481dc86e40bba90403c76f8e144aa9ff04c1da2164299d0298573835f0991181", + "transactionIndex": "0x0", + "blockHash": "0xef0730448490304e5403be0fa8f8ce64f118e9adcca60c07a2ae1ab921d748af", + "blockNumber": "0x1", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "cumulativeGasUsed": "0x13f3a", + "gasUsed": "0x13f3a", + "contractAddress": "0x5fbdb2315678afecb367f032d93f642f64180aa3", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x6a187183545b8a9e7f1790e847139379bf5622baff2cb43acf3f5c79470af782", + "transactionIndex": "0x0", + "blockHash": "0xf3acb96a90071640c2a8c067ae4e16aad87e634ea8d8bbbb5b352fba86ba0148", + "blockNumber": "0x2", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "cumulativeGasUsed": "0x45d80", + "gasUsed": "0x45d80", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x064ad173b4867bdef2fb60060bbdaf01735fbf10414541ea857772974e74ea9d", + "transactionIndex": "0x0", + "blockHash": "0x8373d02109d3ee06a0225f23da4c161c656ccc48fe0fcee931d325508ae73e58", + "blockNumber": "0x3", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "cumulativeGasUsed": "0x45feb", + "gasUsed": "0x45feb", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", + "transactionIndex": "0x0", + "blockHash": "0x16712fae5c0e18f75045f84363fb6b4d9a9fe25e660c4ce286833a533c97f629", + "blockNumber": "0x4", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "cumulativeGasUsed": "0x5905", + "gasUsed": "0x5905", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", + "transactionIndex": "0x0", + "blockHash": "0x156b88c3eb9a1244ba00a1834f3f70de735b39e3e59006dd03af4fe7d5480c11", + "blockNumber": "0x5", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "cumulativeGasUsed": "0xa9c4", + "gasUsed": "0xa9c4", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "transactionIndex": "0x0", + "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", + "blockNumber": "0x6", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "cumulativeGasUsed": "0x66c5", + "gasUsed": "0x66c5", + "contractAddress": null, + "logs": [ + { + "address": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "topics": [ + "0x0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000", + "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", + "blockNumber": "0x6", + "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "transactionIndex": "0x1", + "logIndex": "0x0", + "transactionLogIndex": "0x0", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x11fbb10230c168ca1e36a7e5c69a6dbcd04fd9e64ede39d10a83e36ee8065c16", + "transactionIndex": "0x0", + "blockHash": "0xf1e0ed2eda4e923626ec74621006ed50b3fc27580dc7b4cf68a07ca77420e29c", + "blockNumber": "0x7", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x0000000000000000000000000000000000001337", + "cumulativeGasUsed": "0x5208", + "gasUsed": "0x5208", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + } + ], + "libraries": [ + "src/Broadcast.t.sol:F:0x5fbdb2315678afecb367f032d93f642f64180aa3" + ], + "pending": [], + "path": "broadcast/Broadcast.t.sol/31337/run-latest.json", + "returns": {}, + "timestamp": 1655140035 +} diff --git a/lib/solmate/.gitattributes b/lib/solmate/.gitattributes new file mode 100644 index 0000000..e664563 --- /dev/null +++ b/lib/solmate/.gitattributes @@ -0,0 +1,2 @@ +*.sol linguist-language=Solidity +.gas-snapshot linguist-language=Julia \ No newline at end of file diff --git a/lib/solmate/.github/pull_request_template.md b/lib/solmate/.github/pull_request_template.md new file mode 100644 index 0000000..5cca391 --- /dev/null +++ b/lib/solmate/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Description + +Describe the changes made in your pull request here. + +## Checklist + +Ensure you completed **all of the steps** below before submitting your pull request: + +- [ ] Ran `forge snapshot`? +- [ ] Ran `npm run lint`? +- [ ] Ran `forge test`? + +_Pull requests with an incomplete checklist will be thrown out._ diff --git a/lib/solmate/.github/workflows/tests.yml b/lib/solmate/.github/workflows/tests.yml new file mode 100644 index 0000000..2a29890 --- /dev/null +++ b/lib/solmate/.github/workflows/tests.yml @@ -0,0 +1,29 @@ +name: Tests + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install dependencies + run: forge install + + - name: Check contract sizes + run: forge build --sizes + + - name: Check gas snapshots + run: forge snapshot --check + + - name: Run tests + run: forge test + env: + # Only fuzz intensely if we're running this action on a push to main or for a PR going into main: + FOUNDRY_PROFILE: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && 'intense' }} diff --git a/lib/solmate/.gitignore b/lib/solmate/.gitignore new file mode 100644 index 0000000..3b89382 --- /dev/null +++ b/lib/solmate/.gitignore @@ -0,0 +1,4 @@ +/cache +/node_modules +/out +.DS_STORE \ No newline at end of file diff --git a/lib/solmate/.gitmodules b/lib/solmate/.gitmodules new file mode 100644 index 0000000..e124719 --- /dev/null +++ b/lib/solmate/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/ds-test"] + path = lib/ds-test + url = https://github.com/dapphub/ds-test diff --git a/lib/solmate/.prettierignore b/lib/solmate/.prettierignore new file mode 100644 index 0000000..7951405 --- /dev/null +++ b/lib/solmate/.prettierignore @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/lib/solmate/.prettierrc b/lib/solmate/.prettierrc new file mode 100644 index 0000000..15ae8a7 --- /dev/null +++ b/lib/solmate/.prettierrc @@ -0,0 +1,14 @@ +{ + "tabWidth": 2, + "printWidth": 100, + + "overrides": [ + { + "files": "*.sol", + "options": { + "tabWidth": 4, + "printWidth": 120 + } + } + ] +} diff --git a/lib/solmate/LICENSE b/lib/solmate/LICENSE new file mode 100644 index 0000000..64690f0 --- /dev/null +++ b/lib/solmate/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 t11s + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/solmate/README.md b/lib/solmate/README.md new file mode 100644 index 0000000..58b207c --- /dev/null +++ b/lib/solmate/README.md @@ -0,0 +1,66 @@ +# solmate + +**Modern**, **opinionated**, and **gas optimized** building blocks for **smart contract development**. + +## Contracts + +```ml +auth +├─ Owned — "Simple single owner authorization" +├─ Auth — "Flexible and updatable auth pattern" +├─ authorities +│ ├─ RolesAuthority — "Role based Authority that supports up to 256 roles" +│ ├─ MultiRolesAuthority — "Flexible and target agnostic role based Authority" +mixins +├─ ERC4626 — "Minimal ERC4626 tokenized Vault implementation" +tokens +├─ WETH — "Minimalist and modern Wrapped Ether implementation" +├─ ERC20 — "Modern and gas efficient ERC20 + EIP-2612 implementation" +├─ ERC721 — "Modern, minimalist, and gas efficient ERC721 implementation" +├─ ERC1155 — "Minimalist and gas efficient standard ERC1155 implementation" +utils +├─ SSTORE2 - "Library for cheaper reads and writes to persistent storage" +├─ CREATE3 — "Deploy to deterministic addresses without an initcode factor" +├─ SafeCastLib - "Safe unsigned integer casting lib that reverts on overflow" +├─ ReentrancyGuard — "Gas optimized reentrancy protection for smart contracts" +├─ FixedPointMathLib — "Arithmetic library with operations for fixed-point numbers" +├─ Bytes32AddressLib — "Library for converting between addresses and bytes32 values" +├─ SafeTransferLib — "Safe ERC20/ETH transfer lib that handles missing return values" +``` + +## Safety + +This is **experimental software** and is provided on an "as is" and "as available" basis. + +While each [major release has been audited](audits), these contracts are **not designed with user safety** in mind: + +- There are implicit invariants these contracts expect to hold. +- **You can easily shoot yourself in the foot if you're not careful.** +- You should thoroughly read each contract you plan to use top to bottom. + +We **do not give any warranties** and **will not be liable for any loss** incurred through any use of this codebase. + +## Installation + +To install with [**Foundry**](https://github.com/gakonst/foundry): + +```sh +forge install rari-capital/solmate +``` + +To install with [**Hardhat**](https://github.com/nomiclabs/hardhat) or [**Truffle**](https://github.com/trufflesuite/truffle): + +```sh +npm install @rari-capital/solmate +``` + +## Acknowledgements + +These contracts were inspired by or directly modified from many sources, primarily: + +- [Gnosis](https://github.com/gnosis/gp-v2-contracts) +- [Uniswap](https://github.com/Uniswap/uniswap-lib) +- [Dappsys](https://github.com/dapphub/dappsys) +- [Dappsys V2](https://github.com/dapp-org/dappsys-v2) +- [0xSequence](https://github.com/0xSequence) +- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) diff --git a/lib/solmate/audits/v6-Fixed-Point-Solutions.pdf b/lib/solmate/audits/v6-Fixed-Point-Solutions.pdf new file mode 100644 index 0000000..5c42434 Binary files /dev/null and b/lib/solmate/audits/v6-Fixed-Point-Solutions.pdf differ diff --git a/lib/solmate/foundry.toml b/lib/solmate/foundry.toml new file mode 100644 index 0000000..8a4fd22 --- /dev/null +++ b/lib/solmate/foundry.toml @@ -0,0 +1,7 @@ +[default] +solc = "0.8.10" +bytecode_hash = "none" +optimizer_runs = 1000000 + +[intense] +fuzz_runs = 10000 diff --git a/lib/solmate/lib/ds-test/.gitignore b/lib/solmate/lib/ds-test/.gitignore new file mode 100644 index 0000000..63f0b2c --- /dev/null +++ b/lib/solmate/lib/ds-test/.gitignore @@ -0,0 +1,3 @@ +/.dapple +/build +/out diff --git a/lib/solmate/lib/ds-test/LICENSE b/lib/solmate/lib/ds-test/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/solmate/lib/ds-test/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/lib/solmate/lib/ds-test/Makefile b/lib/solmate/lib/ds-test/Makefile new file mode 100644 index 0000000..661dac4 --- /dev/null +++ b/lib/solmate/lib/ds-test/Makefile @@ -0,0 +1,14 @@ +all:; dapp build + +test: + -dapp --use solc:0.4.23 build + -dapp --use solc:0.4.26 build + -dapp --use solc:0.5.17 build + -dapp --use solc:0.6.12 build + -dapp --use solc:0.7.5 build + +demo: + DAPP_SRC=demo dapp --use solc:0.7.5 build + -hevm dapp-test --verbose 3 + +.PHONY: test demo diff --git a/lib/solmate/lib/ds-test/default.nix b/lib/solmate/lib/ds-test/default.nix new file mode 100644 index 0000000..cf65419 --- /dev/null +++ b/lib/solmate/lib/ds-test/default.nix @@ -0,0 +1,4 @@ +{ solidityPackage, dappsys }: solidityPackage { + name = "ds-test"; + src = ./src; +} diff --git a/lib/solmate/lib/ds-test/demo/demo.sol b/lib/solmate/lib/ds-test/demo/demo.sol new file mode 100644 index 0000000..d3a7d81 --- /dev/null +++ b/lib/solmate/lib/ds-test/demo/demo.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.4.23; + +import "../src/test.sol"; + +contract DemoTest is DSTest { + function test_this() public pure { + require(true); + } + function test_logs() public { + emit log("-- log(string)"); + emit log("a string"); + + emit log("-- log_named_uint(string, uint)"); + log_named_uint("uint", 512); + + emit log("-- log_named_int(string, int)"); + log_named_int("int", -512); + + emit log("-- log_named_address(string, address)"); + log_named_address("address", address(this)); + + emit log("-- log_named_bytes32(string, bytes32)"); + log_named_bytes32("bytes32", "a string"); + + emit log("-- log_named_bytes(string, bytes)"); + log_named_bytes("bytes", hex"cafefe"); + + emit log("-- log_named_string(string, string)"); + log_named_string("string", "a string"); + + emit log("-- log_named_decimal_uint(string, uint, uint)"); + log_named_decimal_uint("decimal uint", 1.0e18, 18); + + emit log("-- log_named_decimal_int(string, int, uint)"); + log_named_decimal_int("decimal int", -1.0e18, 18); + } + event log_old_named_uint(bytes32,uint); + function test_old_logs() public { + log_old_named_uint("key", 500); + log_named_bytes32("bkey", "val"); + } + function test_trace() public view { + this.echo("string 1", "string 2"); + } + function test_multiline() public { + emit log("a multiline\\n" "string"); + emit log("a multiline " "string"); + log_bytes("a string"); + log_bytes("a multiline\n" "string"); + log_bytes("a multiline\\n" "string"); + emit log(unicode"Ώ"); + logs(hex"0000"); + log_named_bytes("0x0000", hex"0000"); + logs(hex"ff"); + } + function echo(string memory s1, string memory s2) public pure + returns (string memory, string memory) + { + return (s1, s2); + } + + function prove_this(uint x) public { + log_named_uint("sym x", x); + assertGt(x + 1, 0); + } + + function test_logn() public { + assembly { + log0(0x01, 0x02) + log1(0x01, 0x02, 0x03) + log2(0x01, 0x02, 0x03, 0x04) + log3(0x01, 0x02, 0x03, 0x04, 0x05) + } + } + + event MyEvent(uint, uint indexed, uint, uint indexed); + function test_events() public { + emit MyEvent(1, 2, 3, 4); + } + + function test_asserts() public { + string memory err = "this test has failed!"; + emit log("## assertTrue(bool)\n"); + assertTrue(false); + emit log("\n"); + assertTrue(false, err); + + emit log("\n## assertEq(address,address)\n"); + assertEq(address(this), msg.sender); + emit log("\n"); + assertEq(address(this), msg.sender, err); + + emit log("\n## assertEq32(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(uint,uint)\n"); + assertEq(uint(0), 1); + emit log("\n"); + assertEq(uint(0), 1, err); + + emit log("\n## assertEq(int,int)\n"); + assertEq(-1, -2); + emit log("\n"); + assertEq(-1, -2, err); + + emit log("\n## assertEqDecimal(int,int,uint)\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertEqDecimal(uint,uint,uint)\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGt(uint,uint)\n"); + assertGt(uint(0), 0); + emit log("\n"); + assertGt(uint(0), 0, err); + + emit log("\n## assertGt(int,int)\n"); + assertGt(-1, -1); + emit log("\n"); + assertGt(-1, -1, err); + + emit log("\n## assertGtDecimal(int,int,uint)\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGtDecimal(uint,uint,uint)\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGe(uint,uint)\n"); + assertGe(uint(0), 1); + emit log("\n"); + assertGe(uint(0), 1, err); + + emit log("\n## assertGe(int,int)\n"); + assertGe(-1, 0); + emit log("\n"); + assertGe(-1, 0, err); + + emit log("\n## assertGeDecimal(int,int,uint)\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGeDecimal(uint,uint,uint)\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertLt(uint,uint)\n"); + assertLt(uint(0), 0); + emit log("\n"); + assertLt(uint(0), 0, err); + + emit log("\n## assertLt(int,int)\n"); + assertLt(-1, -1); + emit log("\n"); + assertLt(-1, -1, err); + + emit log("\n## assertLtDecimal(int,int,uint)\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLtDecimal(uint,uint,uint)\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertLe(uint,uint)\n"); + assertLe(uint(1), 0); + emit log("\n"); + assertLe(uint(1), 0, err); + + emit log("\n## assertLe(int,int)\n"); + assertLe(0, -1); + emit log("\n"); + assertLe(0, -1, err); + + emit log("\n## assertLeDecimal(int,int,uint)\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLeDecimal(uint,uint,uint)\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertEq(string,string)\n"); + string memory s1 = "string 1"; + string memory s2 = "string 2"; + assertEq(s1, s2); + emit log("\n"); + assertEq(s1, s2, err); + + emit log("\n## assertEq0(bytes,bytes)\n"); + assertEq0(hex"abcdef01", hex"abcdef02"); + log("\n"); + assertEq0(hex"abcdef01", hex"abcdef02", err); + } +} + +contract DemoTestWithSetUp { + function setUp() public { + } + function test_pass() public pure { + } +} diff --git a/lib/solmate/lib/ds-test/src/test.sol b/lib/solmate/lib/ds-test/src/test.sol new file mode 100644 index 0000000..515a3bd --- /dev/null +++ b/lib/solmate/lib/ds-test/src/test.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity >=0.5.0; + +contract DSTest { + event log (string); + event logs (bytes); + + event log_address (address); + event log_bytes32 (bytes32); + event log_int (int); + event log_uint (uint); + event log_bytes (bytes); + event log_string (string); + + event log_named_address (string key, address val); + event log_named_bytes32 (string key, bytes32 val); + event log_named_decimal_int (string key, int val, uint decimals); + event log_named_decimal_uint (string key, uint val, uint decimals); + event log_named_int (string key, int val); + event log_named_uint (string key, uint val); + event log_named_bytes (string key, bytes val); + event log_named_string (string key, string val); + + bool public IS_TEST = true; + bool private _failed; + + address constant HEVM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + modifier mayRevert() { _; } + modifier testopts(string memory) { _; } + + function failed() public returns (bool) { + if (_failed) { + return _failed; + } else { + bool globalFailed = false; + if (hasHEVMContext()) { + (, bytes memory retdata) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("load(address,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed")) + ) + ); + globalFailed = abi.decode(retdata, (bool)); + } + return globalFailed; + } + } + + function fail() internal { + if (hasHEVMContext()) { + (bool status, ) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("store(address,bytes32,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) + ) + ); + status; // Silence compiler warnings + } + _failed = true; + } + + function hasHEVMContext() internal view returns (bool) { + uint256 hevmCodeSize = 0; + assembly { + hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) + } + return hevmCodeSize > 0; + } + + modifier logs_gas() { + uint startGas = gasleft(); + _; + uint endGas = gasleft(); + emit log_named_uint("gas", startGas - endGas); + } + + function assertTrue(bool condition) internal { + if (!condition) { + emit log("Error: Assertion Failed"); + fail(); + } + } + + function assertTrue(bool condition, string memory err) internal { + if (!condition) { + emit log_named_string("Error", err); + assertTrue(condition); + } + } + + function assertEq(address a, address b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [address]"); + emit log_named_address(" Expected", b); + emit log_named_address(" Actual", a); + fail(); + } + } + function assertEq(address a, address b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes32 a, bytes32 b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [bytes32]"); + emit log_named_bytes32(" Expected", b); + emit log_named_bytes32(" Actual", a); + fail(); + } + } + function assertEq(bytes32 a, bytes32 b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + function assertEq32(bytes32 a, bytes32 b) internal { + assertEq(a, b); + } + function assertEq32(bytes32 a, bytes32 b, string memory err) internal { + assertEq(a, b, err); + } + + function assertEq(int a, int b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + fail(); + } + } + function assertEq(int a, int b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEq(uint a, uint b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + fail(); + } + } + function assertEq(uint a, uint b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEqDecimal(int a, int b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal int]"); + emit log_named_decimal_int(" Expected", b, decimals); + emit log_named_decimal_int(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + function assertEqDecimal(uint a, uint b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Expected", b, decimals); + emit log_named_decimal_uint(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + + function assertGt(uint a, uint b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGt(uint a, uint b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGt(int a, int b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGt(int a, int b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGtDecimal(int a, int b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + function assertGtDecimal(uint a, uint b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + + function assertGe(uint a, uint b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGe(uint a, uint b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGe(int a, int b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGe(int a, int b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGeDecimal(int a, int b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + function assertGeDecimal(uint a, uint b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertLt(uint a, uint b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLt(uint a, uint b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLt(int a, int b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLt(int a, int b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLtDecimal(int a, int b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + function assertLtDecimal(uint a, uint b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + + function assertLe(uint a, uint b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLe(uint a, uint b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLe(int a, int b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLe(int a, int b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLeDecimal(int a, int b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } + } + function assertLeDecimal(uint a, uint b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log("Error: a == b not satisfied [string]"); + emit log_named_string(" Expected", b); + emit log_named_string(" Actual", a); + fail(); + } + } + function assertEq(string memory a, string memory b, string memory err) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { + ok = true; + if (a.length == b.length) { + for (uint i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + ok = false; + } + } + } else { + ok = false; + } + } + function assertEq0(bytes memory a, bytes memory b) internal { + if (!checkEq0(a, b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Expected", b); + emit log_named_bytes(" Actual", a); + fail(); + } + } + function assertEq0(bytes memory a, bytes memory b, string memory err) internal { + if (!checkEq0(a, b)) { + emit log_named_string("Error", err); + assertEq0(a, b); + } + } +} diff --git a/lib/solmate/package.json b/lib/solmate/package.json new file mode 100644 index 0000000..2b36b3e --- /dev/null +++ b/lib/solmate/package.json @@ -0,0 +1,20 @@ +{ + "name": "@rari-capital/solmate", + "license": "MIT", + "version": "7.0.0-alpha.3", + "description": "Modern, opinionated and gas optimized building blocks for smart contract development.", + "files": [ + "src/**/*.sol" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Rari-Capital/solmate.git" + }, + "devDependencies": { + "prettier": "^2.3.1", + "prettier-plugin-solidity": "^1.0.0-beta.13" + }, + "scripts": { + "lint": "prettier --write **.sol" + } +} diff --git a/lib/solmate/src/auth/Auth.sol b/lib/solmate/src/auth/Auth.sol new file mode 100644 index 0000000..ef3a9d0 --- /dev/null +++ b/lib/solmate/src/auth/Auth.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) +/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) +abstract contract Auth { + event OwnerUpdated(address indexed user, address indexed newOwner); + + event AuthorityUpdated(address indexed user, Authority indexed newAuthority); + + address public owner; + + Authority public authority; + + constructor(address _owner, Authority _authority) { + owner = _owner; + authority = _authority; + + emit OwnerUpdated(msg.sender, _owner); + emit AuthorityUpdated(msg.sender, _authority); + } + + modifier requiresAuth() virtual { + require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED"); + + _; + } + + function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) { + Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas. + + // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be + // aware that this makes protected functions uncallable even to the owner if the authority is out of order. + return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner; + } + + function setAuthority(Authority newAuthority) public virtual { + // We check if the caller is the owner first because we want to ensure they can + // always swap out the authority even if it's reverting or using up a lot of gas. + require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig)); + + authority = newAuthority; + + emit AuthorityUpdated(msg.sender, newAuthority); + } + + function setOwner(address newOwner) public virtual requiresAuth { + owner = newOwner; + + emit OwnerUpdated(msg.sender, newOwner); + } +} + +/// @notice A generic interface for a contract which provides authorization data to an Auth instance. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) +/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) +interface Authority { + function canCall( + address user, + address target, + bytes4 functionSig + ) external view returns (bool); +} diff --git a/lib/solmate/src/auth/Owned.sol b/lib/solmate/src/auth/Owned.sol new file mode 100644 index 0000000..ef8b1ba --- /dev/null +++ b/lib/solmate/src/auth/Owned.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Simple single owner authorization mixin. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Owned.sol) +abstract contract Owned { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event OwnerUpdated(address indexed user, address indexed newOwner); + + /*////////////////////////////////////////////////////////////// + OWNERSHIP STORAGE + //////////////////////////////////////////////////////////////*/ + + address public owner; + + modifier onlyOwner() virtual { + require(msg.sender == owner, "UNAUTHORIZED"); + + _; + } + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(address _owner) { + owner = _owner; + + emit OwnerUpdated(address(0), _owner); + } + + /*////////////////////////////////////////////////////////////// + OWNERSHIP LOGIC + //////////////////////////////////////////////////////////////*/ + + function setOwner(address newOwner) public virtual onlyOwner { + owner = newOwner; + + emit OwnerUpdated(msg.sender, newOwner); + } +} diff --git a/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol b/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol new file mode 100644 index 0000000..6ae1a46 --- /dev/null +++ b/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {Auth, Authority} from "../Auth.sol"; + +/// @notice Flexible and target agnostic role based Authority that supports up to 256 roles. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/MultiRolesAuthority.sol) +contract MultiRolesAuthority is Auth, Authority { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); + + event PublicCapabilityUpdated(bytes4 indexed functionSig, bool enabled); + + event RoleCapabilityUpdated(uint8 indexed role, bytes4 indexed functionSig, bool enabled); + + event TargetCustomAuthorityUpdated(address indexed target, Authority indexed authority); + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} + + /*////////////////////////////////////////////////////////////// + CUSTOM TARGET AUTHORITY STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => Authority) public getTargetCustomAuthority; + + /*////////////////////////////////////////////////////////////// + ROLE/USER STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => bytes32) public getUserRoles; + + mapping(bytes4 => bool) public isCapabilityPublic; + + mapping(bytes4 => bytes32) public getRolesWithCapability; + + function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { + return (uint256(getUserRoles[user]) >> role) & 1 != 0; + } + + function doesRoleHaveCapability(uint8 role, bytes4 functionSig) public view virtual returns (bool) { + return (uint256(getRolesWithCapability[functionSig]) >> role) & 1 != 0; + } + + /*////////////////////////////////////////////////////////////// + AUTHORIZATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function canCall( + address user, + address target, + bytes4 functionSig + ) public view virtual override returns (bool) { + Authority customAuthority = getTargetCustomAuthority[target]; + + if (address(customAuthority) != address(0)) return customAuthority.canCall(user, target, functionSig); + + return + isCapabilityPublic[functionSig] || bytes32(0) != getUserRoles[user] & getRolesWithCapability[functionSig]; + } + + /*/////////////////////////////////////////////////////////////// + CUSTOM TARGET AUTHORITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setTargetCustomAuthority(address target, Authority customAuthority) public virtual requiresAuth { + getTargetCustomAuthority[target] = customAuthority; + + emit TargetCustomAuthorityUpdated(target, customAuthority); + } + + /*////////////////////////////////////////////////////////////// + PUBLIC CAPABILITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setPublicCapability(bytes4 functionSig, bool enabled) public virtual requiresAuth { + isCapabilityPublic[functionSig] = enabled; + + emit PublicCapabilityUpdated(functionSig, enabled); + } + + /*////////////////////////////////////////////////////////////// + USER ROLE ASSIGNMENT LOGIC + //////////////////////////////////////////////////////////////*/ + + function setUserRole( + address user, + uint8 role, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getUserRoles[user] |= bytes32(1 << role); + } else { + getUserRoles[user] &= ~bytes32(1 << role); + } + + emit UserRoleUpdated(user, role, enabled); + } + + /*////////////////////////////////////////////////////////////// + ROLE CAPABILITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setRoleCapability( + uint8 role, + bytes4 functionSig, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getRolesWithCapability[functionSig] |= bytes32(1 << role); + } else { + getRolesWithCapability[functionSig] &= ~bytes32(1 << role); + } + + emit RoleCapabilityUpdated(role, functionSig, enabled); + } +} diff --git a/lib/solmate/src/auth/authorities/RolesAuthority.sol b/lib/solmate/src/auth/authorities/RolesAuthority.sol new file mode 100644 index 0000000..e42a263 --- /dev/null +++ b/lib/solmate/src/auth/authorities/RolesAuthority.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {Auth, Authority} from "../Auth.sol"; + +/// @notice Role based Authority that supports up to 256 roles. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/RolesAuthority.sol) +/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol) +contract RolesAuthority is Auth, Authority { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); + + event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled); + + event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled); + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} + + /*////////////////////////////////////////////////////////////// + ROLE/USER STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => bytes32) public getUserRoles; + + mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic; + + mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability; + + function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { + return (uint256(getUserRoles[user]) >> role) & 1 != 0; + } + + function doesRoleHaveCapability( + uint8 role, + address target, + bytes4 functionSig + ) public view virtual returns (bool) { + return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0; + } + + /*////////////////////////////////////////////////////////////// + AUTHORIZATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function canCall( + address user, + address target, + bytes4 functionSig + ) public view virtual override returns (bool) { + return + isCapabilityPublic[target][functionSig] || + bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig]; + } + + /*////////////////////////////////////////////////////////////// + ROLE CAPABILITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setPublicCapability( + address target, + bytes4 functionSig, + bool enabled + ) public virtual requiresAuth { + isCapabilityPublic[target][functionSig] = enabled; + + emit PublicCapabilityUpdated(target, functionSig, enabled); + } + + function setRoleCapability( + uint8 role, + address target, + bytes4 functionSig, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getRolesWithCapability[target][functionSig] |= bytes32(1 << role); + } else { + getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role); + } + + emit RoleCapabilityUpdated(role, target, functionSig, enabled); + } + + /*////////////////////////////////////////////////////////////// + USER ROLE ASSIGNMENT LOGIC + //////////////////////////////////////////////////////////////*/ + + function setUserRole( + address user, + uint8 role, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getUserRoles[user] |= bytes32(1 << role); + } else { + getUserRoles[user] &= ~bytes32(1 << role); + } + + emit UserRoleUpdated(user, role, enabled); + } +} diff --git a/lib/solmate/src/mixins/ERC4626.sol b/lib/solmate/src/mixins/ERC4626.sol new file mode 100644 index 0000000..35488b2 --- /dev/null +++ b/lib/solmate/src/mixins/ERC4626.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC20} from "../tokens/ERC20.sol"; +import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; +import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; + +/// @notice Minimal ERC4626 tokenized Vault implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol) +abstract contract ERC4626 is ERC20 { + using SafeTransferLib for ERC20; + using FixedPointMathLib for uint256; + + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); + + event Withdraw( + address indexed caller, + address indexed receiver, + address indexed owner, + uint256 assets, + uint256 shares + ); + + /*////////////////////////////////////////////////////////////// + IMMUTABLES + //////////////////////////////////////////////////////////////*/ + + ERC20 public immutable asset; + + constructor( + ERC20 _asset, + string memory _name, + string memory _symbol + ) ERC20(_name, _symbol, _asset.decimals()) { + asset = _asset; + } + + /*////////////////////////////////////////////////////////////// + DEPOSIT/WITHDRAWAL LOGIC + //////////////////////////////////////////////////////////////*/ + + function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { + // Check for rounding error since we round down in previewDeposit. + require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); + + // Need to transfer before minting or ERC777s could reenter. + asset.safeTransferFrom(msg.sender, address(this), assets); + + _mint(receiver, shares); + + emit Deposit(msg.sender, receiver, assets, shares); + + afterDeposit(assets, shares); + } + + function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { + assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. + + // Need to transfer before minting or ERC777s could reenter. + asset.safeTransferFrom(msg.sender, address(this), assets); + + _mint(receiver, shares); + + emit Deposit(msg.sender, receiver, assets, shares); + + afterDeposit(assets, shares); + } + + function withdraw( + uint256 assets, + address receiver, + address owner + ) public virtual returns (uint256 shares) { + shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. + + if (msg.sender != owner) { + uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; + } + + beforeWithdraw(assets, shares); + + _burn(owner, shares); + + emit Withdraw(msg.sender, receiver, owner, assets, shares); + + asset.safeTransfer(receiver, assets); + } + + function redeem( + uint256 shares, + address receiver, + address owner + ) public virtual returns (uint256 assets) { + if (msg.sender != owner) { + uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; + } + + // Check for rounding error since we round down in previewRedeem. + require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); + + beforeWithdraw(assets, shares); + + _burn(owner, shares); + + emit Withdraw(msg.sender, receiver, owner, assets, shares); + + asset.safeTransfer(receiver, assets); + } + + /*////////////////////////////////////////////////////////////// + ACCOUNTING LOGIC + //////////////////////////////////////////////////////////////*/ + + function totalAssets() public view virtual returns (uint256); + + function convertToShares(uint256 assets) public view virtual returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); + } + + function convertToAssets(uint256 shares) public view virtual returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); + } + + function previewDeposit(uint256 assets) public view virtual returns (uint256) { + return convertToShares(assets); + } + + function previewMint(uint256 shares) public view virtual returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); + } + + function previewWithdraw(uint256 assets) public view virtual returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); + } + + function previewRedeem(uint256 shares) public view virtual returns (uint256) { + return convertToAssets(shares); + } + + /*////////////////////////////////////////////////////////////// + DEPOSIT/WITHDRAWAL LIMIT LOGIC + //////////////////////////////////////////////////////////////*/ + + function maxDeposit(address) public view virtual returns (uint256) { + return type(uint256).max; + } + + function maxMint(address) public view virtual returns (uint256) { + return type(uint256).max; + } + + function maxWithdraw(address owner) public view virtual returns (uint256) { + return convertToAssets(balanceOf[owner]); + } + + function maxRedeem(address owner) public view virtual returns (uint256) { + return balanceOf[owner]; + } + + /*////////////////////////////////////////////////////////////// + INTERNAL HOOKS LOGIC + //////////////////////////////////////////////////////////////*/ + + function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} + + function afterDeposit(uint256 assets, uint256 shares) internal virtual {} +} diff --git a/lib/solmate/src/test/Auth.t.sol b/lib/solmate/src/test/Auth.t.sol new file mode 100644 index 0000000..00e29cc --- /dev/null +++ b/lib/solmate/src/test/Auth.t.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {MockAuthChild} from "./utils/mocks/MockAuthChild.sol"; +import {MockAuthority} from "./utils/mocks/MockAuthority.sol"; + +import {Authority} from "../auth/Auth.sol"; + +contract OutOfOrderAuthority is Authority { + function canCall( + address, + address, + bytes4 + ) public pure override returns (bool) { + revert("OUT_OF_ORDER"); + } +} + +contract AuthTest is DSTestPlus { + MockAuthChild mockAuthChild; + + function setUp() public { + mockAuthChild = new MockAuthChild(); + } + + function testSetOwnerAsOwner() public { + mockAuthChild.setOwner(address(0xBEEF)); + assertEq(mockAuthChild.owner(), address(0xBEEF)); + } + + function testSetAuthorityAsOwner() public { + mockAuthChild.setAuthority(Authority(address(0xBEEF))); + assertEq(address(mockAuthChild.authority()), address(0xBEEF)); + } + + function testCallFunctionAsOwner() public { + mockAuthChild.updateFlag(); + } + + function testSetOwnerWithPermissiveAuthority() public { + mockAuthChild.setAuthority(new MockAuthority(true)); + mockAuthChild.setOwner(address(0)); + mockAuthChild.setOwner(address(this)); + } + + function testSetAuthorityWithPermissiveAuthority() public { + mockAuthChild.setAuthority(new MockAuthority(true)); + mockAuthChild.setOwner(address(0)); + mockAuthChild.setAuthority(Authority(address(0xBEEF))); + } + + function testCallFunctionWithPermissiveAuthority() public { + mockAuthChild.setAuthority(new MockAuthority(true)); + mockAuthChild.setOwner(address(0)); + mockAuthChild.updateFlag(); + } + + function testSetAuthorityAsOwnerWithOutOfOrderAuthority() public { + mockAuthChild.setAuthority(new OutOfOrderAuthority()); + mockAuthChild.setAuthority(new MockAuthority(true)); + } + + function testFailSetOwnerAsNonOwner() public { + mockAuthChild.setOwner(address(0)); + mockAuthChild.setOwner(address(0xBEEF)); + } + + function testFailSetAuthorityAsNonOwner() public { + mockAuthChild.setOwner(address(0)); + mockAuthChild.setAuthority(Authority(address(0xBEEF))); + } + + function testFailCallFunctionAsNonOwner() public { + mockAuthChild.setOwner(address(0)); + mockAuthChild.updateFlag(); + } + + function testFailSetOwnerWithRestrictiveAuthority() public { + mockAuthChild.setAuthority(new MockAuthority(false)); + mockAuthChild.setOwner(address(0)); + mockAuthChild.setOwner(address(this)); + } + + function testFailSetAuthorityWithRestrictiveAuthority() public { + mockAuthChild.setAuthority(new MockAuthority(false)); + mockAuthChild.setOwner(address(0)); + mockAuthChild.setAuthority(Authority(address(0xBEEF))); + } + + function testFailCallFunctionWithRestrictiveAuthority() public { + mockAuthChild.setAuthority(new MockAuthority(false)); + mockAuthChild.setOwner(address(0)); + mockAuthChild.updateFlag(); + } + + function testFailSetOwnerAsOwnerWithOutOfOrderAuthority() public { + mockAuthChild.setAuthority(new OutOfOrderAuthority()); + mockAuthChild.setOwner(address(0)); + } + + function testFailCallFunctionAsOwnerWithOutOfOrderAuthority() public { + mockAuthChild.setAuthority(new OutOfOrderAuthority()); + mockAuthChild.updateFlag(); + } + + function testFuzzSetOwnerAsOwner(address newOwner) public { + mockAuthChild.setOwner(newOwner); + assertEq(mockAuthChild.owner(), newOwner); + } + + function testFuzzSetAuthorityAsOwner(Authority newAuthority) public { + mockAuthChild.setAuthority(newAuthority); + assertEq(address(mockAuthChild.authority()), address(newAuthority)); + } + + function testFuzzSetOwnerWithPermissiveAuthority(address deadOwner, address newOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new MockAuthority(true)); + mockAuthChild.setOwner(deadOwner); + mockAuthChild.setOwner(newOwner); + } + + function testFuzzSetAuthorityWithPermissiveAuthority(address deadOwner, Authority newAuthority) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new MockAuthority(true)); + mockAuthChild.setOwner(deadOwner); + mockAuthChild.setAuthority(newAuthority); + } + + function testFuzzCallFunctionWithPermissiveAuthority(address deadOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new MockAuthority(true)); + mockAuthChild.setOwner(deadOwner); + mockAuthChild.updateFlag(); + } + + function testFailFuzzSetOwnerAsNonOwner(address deadOwner, address newOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setOwner(deadOwner); + mockAuthChild.setOwner(newOwner); + } + + function testFailFuzzSetAuthorityAsNonOwner(address deadOwner, Authority newAuthority) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setOwner(deadOwner); + mockAuthChild.setAuthority(newAuthority); + } + + function testFailFuzzCallFunctionAsNonOwner(address deadOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setOwner(deadOwner); + mockAuthChild.updateFlag(); + } + + function testFailFuzzSetOwnerWithRestrictiveAuthority(address deadOwner, address newOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new MockAuthority(false)); + mockAuthChild.setOwner(deadOwner); + mockAuthChild.setOwner(newOwner); + } + + function testFailFuzzSetAuthorityWithRestrictiveAuthority(address deadOwner, Authority newAuthority) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new MockAuthority(false)); + mockAuthChild.setOwner(deadOwner); + mockAuthChild.setAuthority(newAuthority); + } + + function testFailFuzzCallFunctionWithRestrictiveAuthority(address deadOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new MockAuthority(false)); + mockAuthChild.setOwner(deadOwner); + mockAuthChild.updateFlag(); + } + + function testFailFuzzSetOwnerAsOwnerWithOutOfOrderAuthority(address deadOwner) public { + if (deadOwner == address(this)) deadOwner = address(0); + + mockAuthChild.setAuthority(new OutOfOrderAuthority()); + mockAuthChild.setOwner(deadOwner); + } +} diff --git a/lib/solmate/src/test/Bytes32AddressLib.t.sol b/lib/solmate/src/test/Bytes32AddressLib.t.sol new file mode 100644 index 0000000..50d8f97 --- /dev/null +++ b/lib/solmate/src/test/Bytes32AddressLib.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {Bytes32AddressLib} from "../utils/Bytes32AddressLib.sol"; + +contract Bytes32AddressLibTest is DSTestPlus { + function testFillLast12Bytes() public { + assertEq( + Bytes32AddressLib.fillLast12Bytes(0xfEEDFaCEcaFeBEEFfEEDFACecaFEBeeFfeEdfAce), + 0xfeedfacecafebeeffeedfacecafebeeffeedface000000000000000000000000 + ); + } + + function testFromLast20Bytes() public { + assertEq( + Bytes32AddressLib.fromLast20Bytes(0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef), + 0xCAfeBeefFeedfAceCAFeBEEffEEDfaCecafEBeeF + ); + } +} diff --git a/lib/solmate/src/test/CREATE3.t.sol b/lib/solmate/src/test/CREATE3.t.sol new file mode 100644 index 0000000..64e2575 --- /dev/null +++ b/lib/solmate/src/test/CREATE3.t.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {WETH} from "../tokens/WETH.sol"; +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {MockERC20} from "./utils/mocks/MockERC20.sol"; +import {MockAuthChild} from "./utils/mocks/MockAuthChild.sol"; + +import {CREATE3} from "../utils/CREATE3.sol"; + +contract CREATE3Test is DSTestPlus { + function testDeployERC20() public { + bytes32 salt = keccak256(bytes("A salt!")); + + MockERC20 deployed = MockERC20( + CREATE3.deploy( + salt, + abi.encodePacked(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)), + 0 + ) + ); + + assertEq(address(deployed), CREATE3.getDeployed(salt)); + + assertEq(deployed.name(), "Mock Token"); + assertEq(deployed.symbol(), "MOCK"); + assertEq(deployed.decimals(), 18); + } + + function testFailDoubleDeploySameBytecode() public { + bytes32 salt = keccak256(bytes("Salty...")); + + CREATE3.deploy(salt, type(MockAuthChild).creationCode, 0); + CREATE3.deploy(salt, type(MockAuthChild).creationCode, 0); + } + + function testFailDoubleDeployDifferentBytecode() public { + bytes32 salt = keccak256(bytes("and sweet!")); + + CREATE3.deploy(salt, type(WETH).creationCode, 0); + CREATE3.deploy(salt, type(MockAuthChild).creationCode, 0); + } + + function testFuzzDeployERC20( + bytes32 salt, + string calldata name, + string calldata symbol, + uint8 decimals + ) public { + MockERC20 deployed = MockERC20( + CREATE3.deploy(salt, abi.encodePacked(type(MockERC20).creationCode, abi.encode(name, symbol, decimals)), 0) + ); + + assertEq(address(deployed), CREATE3.getDeployed(salt)); + + assertEq(deployed.name(), name); + assertEq(deployed.symbol(), symbol); + assertEq(deployed.decimals(), decimals); + } + + function testFailFuzzDoubleDeploySameBytecode(bytes32 salt, bytes calldata bytecode) public { + CREATE3.deploy(salt, bytecode, 0); + CREATE3.deploy(salt, bytecode, 0); + } + + function testFailFuzzDoubleDeployDifferentBytecode( + bytes32 salt, + bytes calldata bytecode1, + bytes calldata bytecode2 + ) public { + CREATE3.deploy(salt, bytecode1, 0); + CREATE3.deploy(salt, bytecode2, 0); + } +} diff --git a/lib/solmate/src/test/DSTestPlus.t.sol b/lib/solmate/src/test/DSTestPlus.t.sol new file mode 100644 index 0000000..4842abf --- /dev/null +++ b/lib/solmate/src/test/DSTestPlus.t.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +contract DSTestPlusTest is DSTestPlus { + function testBound() public { + assertEq(bound(0, 69, 69), 69); + assertEq(bound(0, 68, 69), 68); + assertEq(bound(5, 0, 4), 0); + assertEq(bound(9999, 1337, 6666), 6006); + assertEq(bound(0, type(uint256).max - 6, type(uint256).max), type(uint256).max - 6); + assertEq(bound(6, type(uint256).max - 6, type(uint256).max), type(uint256).max); + } + + function testFailBoundMinBiggerThanMax() public { + bound(5, 100, 10); + } + + function testRelApproxEqBothZeroesPasses() public { + assertRelApproxEq(0, 0, 1e18); + assertRelApproxEq(0, 0, 0); + } + + function testFuzzBound( + uint256 num, + uint256 min, + uint256 max + ) public { + if (min > max) (min, max) = (max, min); + + uint256 bounded = bound(num, min, max); + + assertGe(bounded, min); + assertLe(bounded, max); + } + + function testFailFuzzBoundMinBiggerThanMax( + uint256 num, + uint256 min, + uint256 max + ) public { + if (max == min) { + unchecked { + min++; // Overflow is handled below. + } + } + + if (max > min) (min, max) = (max, min); + + bound(num, min, max); + } + + function testBrutalizeMemory() public brutalizeMemory("FEEDFACECAFEBEEFFEEDFACECAFEBEEF") { + bytes32 scratchSpace1; + bytes32 scratchSpace2; + bytes32 freeMem1; + bytes32 freeMem2; + + assembly { + scratchSpace1 := mload(0) + scratchSpace2 := mload(32) + freeMem1 := mload(mload(0x40)) + freeMem2 := mload(add(mload(0x40), 32)) + } + + assertGt(uint256(freeMem1), 0); + assertGt(uint256(freeMem2), 0); + assertGt(uint256(scratchSpace1), 0); + assertGt(uint256(scratchSpace2), 0); + } + + function testMeasuringGas() public { + startMeasuringGas(); + stopMeasuringGas(); + } +} diff --git a/lib/solmate/src/test/ERC1155.t.sol b/lib/solmate/src/test/ERC1155.t.sol new file mode 100644 index 0000000..dd76a49 --- /dev/null +++ b/lib/solmate/src/test/ERC1155.t.sol @@ -0,0 +1,1773 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; + +import {MockERC1155} from "./utils/mocks/MockERC1155.sol"; + +import {ERC1155TokenReceiver} from "../tokens/ERC1155.sol"; + +contract ERC1155Recipient is ERC1155TokenReceiver { + address public operator; + address public from; + uint256 public id; + uint256 public amount; + bytes public mintData; + + function onERC1155Received( + address _operator, + address _from, + uint256 _id, + uint256 _amount, + bytes calldata _data + ) public override returns (bytes4) { + operator = _operator; + from = _from; + id = _id; + amount = _amount; + mintData = _data; + + return ERC1155TokenReceiver.onERC1155Received.selector; + } + + address public batchOperator; + address public batchFrom; + uint256[] internal _batchIds; + uint256[] internal _batchAmounts; + bytes public batchData; + + function batchIds() external view returns (uint256[] memory) { + return _batchIds; + } + + function batchAmounts() external view returns (uint256[] memory) { + return _batchAmounts; + } + + function onERC1155BatchReceived( + address _operator, + address _from, + uint256[] calldata _ids, + uint256[] calldata _amounts, + bytes calldata _data + ) external override returns (bytes4) { + batchOperator = _operator; + batchFrom = _from; + _batchIds = _ids; + _batchAmounts = _amounts; + batchData = _data; + + return ERC1155TokenReceiver.onERC1155BatchReceived.selector; + } +} + +contract RevertingERC1155Recipient is ERC1155TokenReceiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) public pure override returns (bytes4) { + revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155Received.selector))); + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure override returns (bytes4) { + revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155BatchReceived.selector))); + } +} + +contract WrongReturnDataERC1155Recipient is ERC1155TokenReceiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) public pure override returns (bytes4) { + return 0xCAFEBEEF; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure override returns (bytes4) { + return 0xCAFEBEEF; + } +} + +contract NonERC1155Recipient {} + +contract ERC1155Test is DSTestPlus, ERC1155TokenReceiver { + MockERC1155 token; + + mapping(address => mapping(uint256 => uint256)) public userMintAmounts; + mapping(address => mapping(uint256 => uint256)) public userTransferOrBurnAmounts; + + function setUp() public { + token = new MockERC1155(); + } + + function testMintToEOA() public { + token.mint(address(0xBEEF), 1337, 1, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 1); + } + + function testMintToERC1155Recipient() public { + ERC1155Recipient to = new ERC1155Recipient(); + + token.mint(address(to), 1337, 1, "testing 123"); + + assertEq(token.balanceOf(address(to), 1337), 1); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), 1337); + assertBytesEq(to.mintData(), "testing 123"); + } + + function testBatchMintToEOA() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory amounts = new uint256[](5); + amounts[0] = 100; + amounts[1] = 200; + amounts[2] = 300; + amounts[3] = 400; + amounts[4] = 500; + + token.batchMint(address(0xBEEF), ids, amounts, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 100); + assertEq(token.balanceOf(address(0xBEEF), 1338), 200); + assertEq(token.balanceOf(address(0xBEEF), 1339), 300); + assertEq(token.balanceOf(address(0xBEEF), 1340), 400); + assertEq(token.balanceOf(address(0xBEEF), 1341), 500); + } + + function testBatchMintToERC1155Recipient() public { + ERC1155Recipient to = new ERC1155Recipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory amounts = new uint256[](5); + amounts[0] = 100; + amounts[1] = 200; + amounts[2] = 300; + amounts[3] = 400; + amounts[4] = 500; + + token.batchMint(address(to), ids, amounts, "testing 123"); + + assertEq(to.batchOperator(), address(this)); + assertEq(to.batchFrom(), address(0)); + assertUintArrayEq(to.batchIds(), ids); + assertUintArrayEq(to.batchAmounts(), amounts); + assertBytesEq(to.batchData(), "testing 123"); + + assertEq(token.balanceOf(address(to), 1337), 100); + assertEq(token.balanceOf(address(to), 1338), 200); + assertEq(token.balanceOf(address(to), 1339), 300); + assertEq(token.balanceOf(address(to), 1340), 400); + assertEq(token.balanceOf(address(to), 1341), 500); + } + + function testBurn() public { + token.mint(address(0xBEEF), 1337, 100, ""); + + token.burn(address(0xBEEF), 1337, 70); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 30); + } + + function testBatchBurn() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory burnAmounts = new uint256[](5); + burnAmounts[0] = 50; + burnAmounts[1] = 100; + burnAmounts[2] = 150; + burnAmounts[3] = 200; + burnAmounts[4] = 250; + + token.batchMint(address(0xBEEF), ids, mintAmounts, ""); + + token.batchBurn(address(0xBEEF), ids, burnAmounts); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 50); + assertEq(token.balanceOf(address(0xBEEF), 1338), 100); + assertEq(token.balanceOf(address(0xBEEF), 1339), 150); + assertEq(token.balanceOf(address(0xBEEF), 1340), 200); + assertEq(token.balanceOf(address(0xBEEF), 1341), 250); + } + + function testApproveAll() public { + token.setApprovalForAll(address(0xBEEF), true); + + assertTrue(token.isApprovedForAll(address(this), address(0xBEEF))); + } + + function testSafeTransferFromToEOA() public { + address from = address(0xABCD); + + token.mint(from, 1337, 100, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(0xBEEF), 1337, 70, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 70); + assertEq(token.balanceOf(from, 1337), 30); + } + + function testSafeTransferFromToERC1155Recipient() public { + ERC1155Recipient to = new ERC1155Recipient(); + + address from = address(0xABCD); + + token.mint(from, 1337, 100, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(to), 1337, 70, "testing 123"); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), from); + assertEq(to.id(), 1337); + assertBytesEq(to.mintData(), "testing 123"); + + assertEq(token.balanceOf(address(to), 1337), 70); + assertEq(token.balanceOf(from, 1337), 30); + } + + function testSafeTransferFromSelf() public { + token.mint(address(this), 1337, 100, ""); + + token.safeTransferFrom(address(this), address(0xBEEF), 1337, 70, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 70); + assertEq(token.balanceOf(address(this), 1337), 30); + } + + function testSafeBatchTransferFromToEOA() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + transferAmounts[4] = 250; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(0xBEEF), ids, transferAmounts, ""); + + assertEq(token.balanceOf(from, 1337), 50); + assertEq(token.balanceOf(address(0xBEEF), 1337), 50); + + assertEq(token.balanceOf(from, 1338), 100); + assertEq(token.balanceOf(address(0xBEEF), 1338), 100); + + assertEq(token.balanceOf(from, 1339), 150); + assertEq(token.balanceOf(address(0xBEEF), 1339), 150); + + assertEq(token.balanceOf(from, 1340), 200); + assertEq(token.balanceOf(address(0xBEEF), 1340), 200); + + assertEq(token.balanceOf(from, 1341), 250); + assertEq(token.balanceOf(address(0xBEEF), 1341), 250); + } + + function testSafeBatchTransferFromToERC1155Recipient() public { + address from = address(0xABCD); + + ERC1155Recipient to = new ERC1155Recipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + transferAmounts[4] = 250; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(to), ids, transferAmounts, "testing 123"); + + assertEq(to.batchOperator(), address(this)); + assertEq(to.batchFrom(), from); + assertUintArrayEq(to.batchIds(), ids); + assertUintArrayEq(to.batchAmounts(), transferAmounts); + assertBytesEq(to.batchData(), "testing 123"); + + assertEq(token.balanceOf(from, 1337), 50); + assertEq(token.balanceOf(address(to), 1337), 50); + + assertEq(token.balanceOf(from, 1338), 100); + assertEq(token.balanceOf(address(to), 1338), 100); + + assertEq(token.balanceOf(from, 1339), 150); + assertEq(token.balanceOf(address(to), 1339), 150); + + assertEq(token.balanceOf(from, 1340), 200); + assertEq(token.balanceOf(address(to), 1340), 200); + + assertEq(token.balanceOf(from, 1341), 250); + assertEq(token.balanceOf(address(to), 1341), 250); + } + + function testBatchBalanceOf() public { + address[] memory tos = new address[](5); + tos[0] = address(0xBEEF); + tos[1] = address(0xCAFE); + tos[2] = address(0xFACE); + tos[3] = address(0xDEAD); + tos[4] = address(0xFEED); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.mint(address(0xBEEF), 1337, 100, ""); + token.mint(address(0xCAFE), 1338, 200, ""); + token.mint(address(0xFACE), 1339, 300, ""); + token.mint(address(0xDEAD), 1340, 400, ""); + token.mint(address(0xFEED), 1341, 500, ""); + + uint256[] memory balances = token.balanceOfBatch(tos, ids); + + assertEq(balances[0], 100); + assertEq(balances[1], 200); + assertEq(balances[2], 300); + assertEq(balances[3], 400); + assertEq(balances[4], 500); + } + + function testFailMintToZero() public { + token.mint(address(0), 1337, 1, ""); + } + + function testFailMintToNonERC155Recipient() public { + token.mint(address(new NonERC1155Recipient()), 1337, 1, ""); + } + + function testFailMintToRevertingERC155Recipient() public { + token.mint(address(new RevertingERC1155Recipient()), 1337, 1, ""); + } + + function testFailMintToWrongReturnDataERC155Recipient() public { + token.mint(address(new RevertingERC1155Recipient()), 1337, 1, ""); + } + + function testFailBurnInsufficientBalance() public { + token.mint(address(0xBEEF), 1337, 70, ""); + token.burn(address(0xBEEF), 1337, 100); + } + + function testFailSafeTransferFromInsufficientBalance() public { + address from = address(0xABCD); + + token.mint(from, 1337, 70, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(0xBEEF), 1337, 100, ""); + } + + function testFailSafeTransferFromSelfInsufficientBalance() public { + token.mint(address(this), 1337, 70, ""); + token.safeTransferFrom(address(this), address(0xBEEF), 1337, 100, ""); + } + + function testFailSafeTransferFromToZero() public { + token.mint(address(this), 1337, 100, ""); + token.safeTransferFrom(address(this), address(0), 1337, 70, ""); + } + + function testFailSafeTransferFromToNonERC155Recipient() public { + token.mint(address(this), 1337, 100, ""); + token.safeTransferFrom(address(this), address(new NonERC1155Recipient()), 1337, 70, ""); + } + + function testFailSafeTransferFromToRevertingERC1155Recipient() public { + token.mint(address(this), 1337, 100, ""); + token.safeTransferFrom(address(this), address(new RevertingERC1155Recipient()), 1337, 70, ""); + } + + function testFailSafeTransferFromToWrongReturnDataERC1155Recipient() public { + token.mint(address(this), 1337, 100, ""); + token.safeTransferFrom(address(this), address(new WrongReturnDataERC1155Recipient()), 1337, 70, ""); + } + + function testFailSafeBatchTransferInsufficientBalance() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + + mintAmounts[0] = 50; + mintAmounts[1] = 100; + mintAmounts[2] = 150; + mintAmounts[3] = 200; + mintAmounts[4] = 250; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 100; + transferAmounts[1] = 200; + transferAmounts[2] = 300; + transferAmounts[3] = 400; + transferAmounts[4] = 500; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(0xBEEF), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToZero() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + transferAmounts[4] = 250; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(0), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToNonERC1155Recipient() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + transferAmounts[4] = 250; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(new NonERC1155Recipient()), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToRevertingERC1155Recipient() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + transferAmounts[4] = 250; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(new RevertingERC1155Recipient()), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + transferAmounts[4] = 250; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(new WrongReturnDataERC1155Recipient()), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromWithArrayLengthMismatch() public { + address from = address(0xABCD); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory transferAmounts = new uint256[](4); + transferAmounts[0] = 50; + transferAmounts[1] = 100; + transferAmounts[2] = 150; + transferAmounts[3] = 200; + + token.batchMint(from, ids, mintAmounts, ""); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(0xBEEF), ids, transferAmounts, ""); + } + + function testFailBatchMintToZero() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + token.batchMint(address(0), ids, mintAmounts, ""); + } + + function testFailBatchMintToNonERC1155Recipient() public { + NonERC1155Recipient to = new NonERC1155Recipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + token.batchMint(address(to), ids, mintAmounts, ""); + } + + function testFailBatchMintToRevertingERC1155Recipient() public { + RevertingERC1155Recipient to = new RevertingERC1155Recipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + token.batchMint(address(to), ids, mintAmounts, ""); + } + + function testFailBatchMintToWrongReturnDataERC1155Recipient() public { + WrongReturnDataERC1155Recipient to = new WrongReturnDataERC1155Recipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + token.batchMint(address(to), ids, mintAmounts, ""); + } + + function testFailBatchMintWithArrayMismatch() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory amounts = new uint256[](4); + amounts[0] = 100; + amounts[1] = 200; + amounts[2] = 300; + amounts[3] = 400; + + token.batchMint(address(0xBEEF), ids, amounts, ""); + } + + function testFailBatchBurnInsufficientBalance() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 50; + mintAmounts[1] = 100; + mintAmounts[2] = 150; + mintAmounts[3] = 200; + mintAmounts[4] = 250; + + uint256[] memory burnAmounts = new uint256[](5); + burnAmounts[0] = 100; + burnAmounts[1] = 200; + burnAmounts[2] = 300; + burnAmounts[3] = 400; + burnAmounts[4] = 500; + + token.batchMint(address(0xBEEF), ids, mintAmounts, ""); + + token.batchBurn(address(0xBEEF), ids, burnAmounts); + } + + function testFailBatchBurnWithArrayLengthMismatch() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory mintAmounts = new uint256[](5); + mintAmounts[0] = 100; + mintAmounts[1] = 200; + mintAmounts[2] = 300; + mintAmounts[3] = 400; + mintAmounts[4] = 500; + + uint256[] memory burnAmounts = new uint256[](4); + burnAmounts[0] = 50; + burnAmounts[1] = 100; + burnAmounts[2] = 150; + burnAmounts[3] = 200; + + token.batchMint(address(0xBEEF), ids, mintAmounts, ""); + + token.batchBurn(address(0xBEEF), ids, burnAmounts); + } + + function testFailBalanceOfBatchWithArrayMismatch() public view { + address[] memory tos = new address[](5); + tos[0] = address(0xBEEF); + tos[1] = address(0xCAFE); + tos[2] = address(0xFACE); + tos[3] = address(0xDEAD); + tos[4] = address(0xFEED); + + uint256[] memory ids = new uint256[](4); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + + token.balanceOfBatch(tos, ids); + } + + function testFuzzMintToEOA( + address to, + uint256 id, + uint256 amount, + bytes memory mintData + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + token.mint(to, id, amount, mintData); + + assertEq(token.balanceOf(to, id), amount); + } + + function testFuzzMintToERC1155Recipient( + uint256 id, + uint256 amount, + bytes memory mintData + ) public { + ERC1155Recipient to = new ERC1155Recipient(); + + token.mint(address(to), id, amount, mintData); + + assertEq(token.balanceOf(address(to), id), amount); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), id); + assertBytesEq(to.mintData(), mintData); + } + + function testFuzzBatchMintToEOA( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + uint256 minLength = min2(ids.length, amounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + normalizedIds[i] = id; + normalizedAmounts[i] = mintAmount; + + userMintAmounts[to][id] += mintAmount; + } + + token.batchMint(to, normalizedIds, normalizedAmounts, mintData); + + for (uint256 i = 0; i < normalizedIds.length; i++) { + uint256 id = normalizedIds[i]; + + assertEq(token.balanceOf(to, id), userMintAmounts[to][id]); + } + } + + function testFuzzBatchMintToERC1155Recipient( + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + ERC1155Recipient to = new ERC1155Recipient(); + + uint256 minLength = min2(ids.length, amounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + normalizedIds[i] = id; + normalizedAmounts[i] = mintAmount; + + userMintAmounts[address(to)][id] += mintAmount; + } + + token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); + + assertEq(to.batchOperator(), address(this)); + assertEq(to.batchFrom(), address(0)); + assertUintArrayEq(to.batchIds(), normalizedIds); + assertUintArrayEq(to.batchAmounts(), normalizedAmounts); + assertBytesEq(to.batchData(), mintData); + + for (uint256 i = 0; i < normalizedIds.length; i++) { + uint256 id = normalizedIds[i]; + + assertEq(token.balanceOf(address(to), id), userMintAmounts[address(to)][id]); + } + } + + function testFuzzBurn( + address to, + uint256 id, + uint256 mintAmount, + bytes memory mintData, + uint256 burnAmount + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + burnAmount = bound(burnAmount, 0, mintAmount); + + token.mint(to, id, mintAmount, mintData); + + token.burn(to, id, burnAmount); + + assertEq(token.balanceOf(address(to), id), mintAmount - burnAmount); + } + + function testFuzzBatchBurn( + address to, + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory burnAmounts, + bytes memory mintData + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + uint256 minLength = min3(ids.length, mintAmounts.length, burnAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedBurnAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; + + normalizedIds[i] = id; + normalizedMintAmounts[i] = bound(mintAmounts[i], 0, remainingMintAmountForId); + normalizedBurnAmounts[i] = bound(burnAmounts[i], 0, normalizedMintAmounts[i]); + + userMintAmounts[address(to)][id] += normalizedMintAmounts[i]; + userTransferOrBurnAmounts[address(to)][id] += normalizedBurnAmounts[i]; + } + + token.batchMint(to, normalizedIds, normalizedMintAmounts, mintData); + + token.batchBurn(to, normalizedIds, normalizedBurnAmounts); + + for (uint256 i = 0; i < normalizedIds.length; i++) { + uint256 id = normalizedIds[i]; + + assertEq(token.balanceOf(to, id), userMintAmounts[to][id] - userTransferOrBurnAmounts[to][id]); + } + } + + function testFuzzApproveAll(address to, bool approved) public { + token.setApprovalForAll(to, approved); + + assertBoolEq(token.isApprovedForAll(address(this), to), approved); + } + + function testFuzzSafeTransferFromToEOA( + uint256 id, + uint256 mintAmount, + bytes memory mintData, + uint256 transferAmount, + address to, + bytes memory transferData + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + transferAmount = bound(transferAmount, 0, mintAmount); + + address from = address(0xABCD); + + token.mint(from, id, mintAmount, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, to, id, transferAmount, transferData); + + assertEq(token.balanceOf(to, id), transferAmount); + assertEq(token.balanceOf(from, id), mintAmount - transferAmount); + } + + function testFuzzSafeTransferFromToERC1155Recipient( + uint256 id, + uint256 mintAmount, + bytes memory mintData, + uint256 transferAmount, + bytes memory transferData + ) public { + ERC1155Recipient to = new ERC1155Recipient(); + + address from = address(0xABCD); + + transferAmount = bound(transferAmount, 0, mintAmount); + + token.mint(from, id, mintAmount, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(to), id, transferAmount, transferData); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), from); + assertEq(to.id(), id); + assertBytesEq(to.mintData(), transferData); + + assertEq(token.balanceOf(address(to), id), transferAmount); + assertEq(token.balanceOf(from, id), mintAmount - transferAmount); + } + + function testFuzzSafeTransferFromSelf( + uint256 id, + uint256 mintAmount, + bytes memory mintData, + uint256 transferAmount, + address to, + bytes memory transferData + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + transferAmount = bound(transferAmount, 0, mintAmount); + + token.mint(address(this), id, mintAmount, mintData); + + token.safeTransferFrom(address(this), to, id, transferAmount, transferData); + + assertEq(token.balanceOf(to, id), transferAmount); + assertEq(token.balanceOf(address(this), id), mintAmount - transferAmount); + } + + function testFuzzSafeBatchTransferFromToEOA( + address to, + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + address from = address(0xABCD); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + userTransferOrBurnAmounts[from][id] += transferAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, to, normalizedIds, normalizedTransferAmounts, transferData); + + for (uint256 i = 0; i < normalizedIds.length; i++) { + uint256 id = normalizedIds[i]; + + assertEq(token.balanceOf(address(to), id), userTransferOrBurnAmounts[from][id]); + assertEq(token.balanceOf(from, id), userMintAmounts[from][id] - userTransferOrBurnAmounts[from][id]); + } + } + + function testFuzzSafeBatchTransferFromToERC1155Recipient( + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + ERC1155Recipient to = new ERC1155Recipient(); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + userTransferOrBurnAmounts[from][id] += transferAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(to), normalizedIds, normalizedTransferAmounts, transferData); + + assertEq(to.batchOperator(), address(this)); + assertEq(to.batchFrom(), from); + assertUintArrayEq(to.batchIds(), normalizedIds); + assertUintArrayEq(to.batchAmounts(), normalizedTransferAmounts); + assertBytesEq(to.batchData(), transferData); + + for (uint256 i = 0; i < normalizedIds.length; i++) { + uint256 id = normalizedIds[i]; + uint256 transferAmount = userTransferOrBurnAmounts[from][id]; + + assertEq(token.balanceOf(address(to), id), transferAmount); + assertEq(token.balanceOf(from, id), userMintAmounts[from][id] - transferAmount); + } + } + + function testFuzzBatchBalanceOf( + address[] memory tos, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + uint256 minLength = min3(tos.length, ids.length, amounts.length); + + address[] memory normalizedTos = new address[](minLength); + uint256[] memory normalizedIds = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + address to = tos[i] == address(0) ? address(0xBEEF) : tos[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; + + normalizedTos[i] = to; + normalizedIds[i] = id; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + token.mint(to, id, mintAmount, mintData); + + userMintAmounts[to][id] += mintAmount; + } + + uint256[] memory balances = token.balanceOfBatch(normalizedTos, normalizedIds); + + for (uint256 i = 0; i < normalizedTos.length; i++) { + assertEq(balances[i], token.balanceOf(normalizedTos[i], normalizedIds[i])); + } + } + + function testFailFuzzMintToZero( + uint256 id, + uint256 amount, + bytes memory data + ) public { + token.mint(address(0), id, amount, data); + } + + function testFailFuzzMintToNonERC155Recipient( + uint256 id, + uint256 mintAmount, + bytes memory mintData + ) public { + token.mint(address(new NonERC1155Recipient()), id, mintAmount, mintData); + } + + function testFailFuzzMintToRevertingERC155Recipient( + uint256 id, + uint256 mintAmount, + bytes memory mintData + ) public { + token.mint(address(new RevertingERC1155Recipient()), id, mintAmount, mintData); + } + + function testFailFuzzMintToWrongReturnDataERC155Recipient( + uint256 id, + uint256 mintAmount, + bytes memory mintData + ) public { + token.mint(address(new RevertingERC1155Recipient()), id, mintAmount, mintData); + } + + function testFailFuzzBurnInsufficientBalance( + address to, + uint256 id, + uint256 mintAmount, + uint256 burnAmount, + bytes memory mintData + ) public { + burnAmount = bound(burnAmount, mintAmount + 1, type(uint256).max); + + token.mint(to, id, mintAmount, mintData); + token.burn(to, id, burnAmount); + } + + function testFailFuzzSafeTransferFromInsufficientBalance( + address to, + uint256 id, + uint256 mintAmount, + uint256 transferAmount, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + transferAmount = bound(transferAmount, mintAmount + 1, type(uint256).max); + + token.mint(from, id, mintAmount, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, to, id, transferAmount, transferData); + } + + function testFailFuzzSafeTransferFromSelfInsufficientBalance( + address to, + uint256 id, + uint256 mintAmount, + uint256 transferAmount, + bytes memory mintData, + bytes memory transferData + ) public { + transferAmount = bound(transferAmount, mintAmount + 1, type(uint256).max); + + token.mint(address(this), id, mintAmount, mintData); + token.safeTransferFrom(address(this), to, id, transferAmount, transferData); + } + + function testFailFuzzSafeTransferFromToZero( + uint256 id, + uint256 mintAmount, + uint256 transferAmount, + bytes memory mintData, + bytes memory transferData + ) public { + transferAmount = bound(transferAmount, 0, mintAmount); + + token.mint(address(this), id, mintAmount, mintData); + token.safeTransferFrom(address(this), address(0), id, transferAmount, transferData); + } + + function testFailFuzzSafeTransferFromToNonERC155Recipient( + uint256 id, + uint256 mintAmount, + uint256 transferAmount, + bytes memory mintData, + bytes memory transferData + ) public { + transferAmount = bound(transferAmount, 0, mintAmount); + + token.mint(address(this), id, mintAmount, mintData); + token.safeTransferFrom(address(this), address(new NonERC1155Recipient()), id, transferAmount, transferData); + } + + function testFailFuzzSafeTransferFromToRevertingERC1155Recipient( + uint256 id, + uint256 mintAmount, + uint256 transferAmount, + bytes memory mintData, + bytes memory transferData + ) public { + transferAmount = bound(transferAmount, 0, mintAmount); + + token.mint(address(this), id, mintAmount, mintData); + token.safeTransferFrom( + address(this), + address(new RevertingERC1155Recipient()), + id, + transferAmount, + transferData + ); + } + + function testFailFuzzSafeTransferFromToWrongReturnDataERC1155Recipient( + uint256 id, + uint256 mintAmount, + uint256 transferAmount, + bytes memory mintData, + bytes memory transferData + ) public { + transferAmount = bound(transferAmount, 0, mintAmount); + + token.mint(address(this), id, mintAmount, mintData); + token.safeTransferFrom( + address(this), + address(new WrongReturnDataERC1155Recipient()), + id, + transferAmount, + transferData + ); + } + + function testFailFuzzSafeBatchTransferInsufficientBalance( + address to, + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + if (minLength == 0) revert(); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], mintAmount + 1, type(uint256).max); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, to, normalizedIds, normalizedTransferAmounts, transferData); + } + + function testFailFuzzSafeBatchTransferFromToZero( + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, address(0), normalizedIds, normalizedTransferAmounts, transferData); + } + + function testFailFuzzSafeBatchTransferFromToNonERC1155Recipient( + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom( + from, + address(new NonERC1155Recipient()), + normalizedIds, + normalizedTransferAmounts, + transferData + ); + } + + function testFailFuzzSafeBatchTransferFromToRevertingERC1155Recipient( + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom( + from, + address(new RevertingERC1155Recipient()), + normalizedIds, + normalizedTransferAmounts, + transferData + ); + } + + function testFailFuzzSafeBatchTransferFromToWrongReturnDataERC1155Recipient( + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedTransferAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; + + uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); + uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); + + normalizedIds[i] = id; + normalizedMintAmounts[i] = mintAmount; + normalizedTransferAmounts[i] = transferAmount; + + userMintAmounts[from][id] += mintAmount; + } + + token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom( + from, + address(new WrongReturnDataERC1155Recipient()), + normalizedIds, + normalizedTransferAmounts, + transferData + ); + } + + function testFailFuzzSafeBatchTransferFromWithArrayLengthMismatch( + address to, + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory transferAmounts, + bytes memory mintData, + bytes memory transferData + ) public { + address from = address(0xABCD); + + if (ids.length == transferAmounts.length) revert(); + + token.batchMint(from, ids, mintAmounts, mintData); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(from, to, ids, transferAmounts, transferData); + } + + function testFailFuzzBatchMintToZero( + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + uint256 minLength = min2(ids.length, amounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(0)][id]; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + normalizedIds[i] = id; + normalizedAmounts[i] = mintAmount; + + userMintAmounts[address(0)][id] += mintAmount; + } + + token.batchMint(address(0), normalizedIds, normalizedAmounts, mintData); + } + + function testFailFuzzBatchMintToNonERC1155Recipient( + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + NonERC1155Recipient to = new NonERC1155Recipient(); + + uint256 minLength = min2(ids.length, amounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + normalizedIds[i] = id; + normalizedAmounts[i] = mintAmount; + + userMintAmounts[address(to)][id] += mintAmount; + } + + token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); + } + + function testFailFuzzBatchMintToRevertingERC1155Recipient( + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + RevertingERC1155Recipient to = new RevertingERC1155Recipient(); + + uint256 minLength = min2(ids.length, amounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + normalizedIds[i] = id; + normalizedAmounts[i] = mintAmount; + + userMintAmounts[address(to)][id] += mintAmount; + } + + token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); + } + + function testFailFuzzBatchMintToWrongReturnDataERC1155Recipient( + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + WrongReturnDataERC1155Recipient to = new WrongReturnDataERC1155Recipient(); + + uint256 minLength = min2(ids.length, amounts.length); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; + + uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); + + normalizedIds[i] = id; + normalizedAmounts[i] = mintAmount; + + userMintAmounts[address(to)][id] += mintAmount; + } + + token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); + } + + function testFailFuzzBatchMintWithArrayMismatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory mintData + ) public { + if (ids.length == amounts.length) revert(); + + token.batchMint(address(to), ids, amounts, mintData); + } + + function testFailFuzzBatchBurnInsufficientBalance( + address to, + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory burnAmounts, + bytes memory mintData + ) public { + uint256 minLength = min3(ids.length, mintAmounts.length, burnAmounts.length); + + if (minLength == 0) revert(); + + uint256[] memory normalizedIds = new uint256[](minLength); + uint256[] memory normalizedMintAmounts = new uint256[](minLength); + uint256[] memory normalizedBurnAmounts = new uint256[](minLength); + + for (uint256 i = 0; i < minLength; i++) { + uint256 id = ids[i]; + + uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; + + normalizedIds[i] = id; + normalizedMintAmounts[i] = bound(mintAmounts[i], 0, remainingMintAmountForId); + normalizedBurnAmounts[i] = bound(burnAmounts[i], normalizedMintAmounts[i] + 1, type(uint256).max); + + userMintAmounts[to][id] += normalizedMintAmounts[i]; + } + + token.batchMint(to, normalizedIds, normalizedMintAmounts, mintData); + + token.batchBurn(to, normalizedIds, normalizedBurnAmounts); + } + + function testFailFuzzBatchBurnWithArrayLengthMismatch( + address to, + uint256[] memory ids, + uint256[] memory mintAmounts, + uint256[] memory burnAmounts, + bytes memory mintData + ) public { + if (ids.length == burnAmounts.length) revert(); + + token.batchMint(to, ids, mintAmounts, mintData); + + token.batchBurn(to, ids, burnAmounts); + } + + function testFailFuzzBalanceOfBatchWithArrayMismatch(address[] memory tos, uint256[] memory ids) public view { + if (tos.length == ids.length) revert(); + + token.balanceOfBatch(tos, ids); + } +} diff --git a/lib/solmate/src/test/ERC1155B.t.sol b/lib/solmate/src/test/ERC1155B.t.sol new file mode 100644 index 0000000..014e409 --- /dev/null +++ b/lib/solmate/src/test/ERC1155B.t.sol @@ -0,0 +1,662 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; + +import {MockERC1155B} from "./utils/mocks/MockERC1155B.sol"; +import {ERC1155BUser} from "./utils/users/ERC1155BUser.sol"; + +import {ERC1155TokenReceiver} from "../tokens/ERC1155.sol"; + +// TODO: test invalid_amount errors +// TODO: test ownerOf() +// TODO: fuzz testing +// TODO: test custom safe batch transfer +// TODO: test cant burn unminted tokens + +contract ERC1155BRecipient is ERC1155TokenReceiver { + address public operator; + address public from; + uint256 public id; + uint256 public amount; + bytes public mintData; + + function onERC1155Received( + address _operator, + address _from, + uint256 _id, + uint256 _amount, + bytes calldata _data + ) public override returns (bytes4) { + operator = _operator; + from = _from; + id = _id; + amount = _amount; + mintData = _data; + + return ERC1155TokenReceiver.onERC1155Received.selector; + } + + address public batchOperator; + address public batchFrom; + uint256[] internal _batchIds; + uint256[] internal _batchAmounts; + bytes public batchData; + + function batchIds() external view returns (uint256[] memory) { + return _batchIds; + } + + function batchAmounts() external view returns (uint256[] memory) { + return _batchAmounts; + } + + function onERC1155BatchReceived( + address _operator, + address _from, + uint256[] calldata _ids, + uint256[] calldata _amounts, + bytes calldata _data + ) external override returns (bytes4) { + batchOperator = _operator; + batchFrom = _from; + _batchIds = _ids; + _batchAmounts = _amounts; + batchData = _data; + + return ERC1155TokenReceiver.onERC1155BatchReceived.selector; + } +} + +contract RevertingERC1155Recipient is ERC1155TokenReceiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) public pure override returns (bytes4) { + revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155Received.selector))); + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure override returns (bytes4) { + revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155BatchReceived.selector))); + } +} + +contract WrongReturnDataERC1155BRecipient is ERC1155TokenReceiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) public pure override returns (bytes4) { + return 0xCAFEBEEF; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure override returns (bytes4) { + return 0xCAFEBEEF; + } +} + +contract NonERC1155BRecipient {} + +contract ERC1155BTest is DSTestPlus, ERC1155TokenReceiver { + MockERC1155B token; + + mapping(address => mapping(uint256 => uint256)) public userMintAmounts; + mapping(address => mapping(uint256 => uint256)) public userTransferOrBurnAmounts; + + function setUp() public { + token = new MockERC1155B(); + } + + function testMintToEOA() public { + token.mint(address(0xBEEF), 1337, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 1); + } + + function testMintToERC1155Recipient() public { + ERC1155BRecipient to = new ERC1155BRecipient(); + + token.mint(address(to), 1337, "testing 123"); + + assertEq(token.balanceOf(address(to), 1337), 1); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), 1337); + assertBytesEq(to.mintData(), "testing 123"); + } + + function testBatchMintToEOA() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchMint(address(0xBEEF), ids, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 1); + assertEq(token.balanceOf(address(0xBEEF), 1338), 1); + assertEq(token.balanceOf(address(0xBEEF), 1339), 1); + assertEq(token.balanceOf(address(0xBEEF), 1340), 1); + assertEq(token.balanceOf(address(0xBEEF), 1341), 1); + } + + function testBatchMintToERC1155Recipient() public { + ERC1155BRecipient to = new ERC1155BRecipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory amounts = new uint256[](5); + amounts[0] = 1; + amounts[1] = 1; + amounts[2] = 1; + amounts[3] = 1; + amounts[4] = 1; + + token.batchMint(address(to), ids, "testing 123"); + + assertEq(to.batchOperator(), address(this)); + assertEq(to.batchFrom(), address(0)); + assertUintArrayEq(to.batchIds(), ids); + assertUintArrayEq(to.batchAmounts(), amounts); + assertBytesEq(to.batchData(), "testing 123"); + + assertEq(token.balanceOf(address(to), 1337), 1); + assertEq(token.balanceOf(address(to), 1338), 1); + assertEq(token.balanceOf(address(to), 1339), 1); + assertEq(token.balanceOf(address(to), 1340), 1); + assertEq(token.balanceOf(address(to), 1341), 1); + } + + function testBurn() public { + token.mint(address(0xBEEF), 1337, ""); + + token.burn(1337); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 0); + } + + function testBatchBurn() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchMint(address(0xBEEF), ids, ""); + + token.batchBurn(address(0xBEEF), ids); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 0); + assertEq(token.balanceOf(address(0xBEEF), 1338), 0); + assertEq(token.balanceOf(address(0xBEEF), 1339), 0); + assertEq(token.balanceOf(address(0xBEEF), 1340), 0); + assertEq(token.balanceOf(address(0xBEEF), 1341), 0); + } + + function testApproveAll() public { + token.setApprovalForAll(address(0xBEEF), true); + + assertTrue(token.isApprovedForAll(address(this), address(0xBEEF))); + } + + function testSafeTransferFromToEOA() public { + ERC1155BUser from = new ERC1155BUser(token); + + token.mint(address(from), 1337, ""); + + from.setApprovalForAll(address(this), true); + + token.safeTransferFrom(address(from), address(0xBEEF), 1337, 1, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 1); + assertEq(token.balanceOf(address(from), 1337), 0); + } + + function testSafeTransferFromToERC1155Recipient() public { + ERC1155BRecipient to = new ERC1155BRecipient(); + + ERC1155BUser from = new ERC1155BUser(token); + + token.mint(address(from), 1337, ""); + + from.setApprovalForAll(address(this), true); + + token.safeTransferFrom(address(from), address(to), 1337, 1, "testing 123"); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(from)); + assertEq(to.id(), 1337); + assertBytesEq(to.mintData(), "testing 123"); + + assertEq(token.balanceOf(address(to), 1337), 1); + assertEq(token.balanceOf(address(from), 1337), 0); + } + + function testSafeTransferFromSelf() public { + token.mint(address(this), 1337, ""); + + token.safeTransferFrom(address(this), address(0xBEEF), 1337, 1, ""); + + assertEq(token.balanceOf(address(0xBEEF), 1337), 1); + assertEq(token.balanceOf(address(this), 1337), 0); + } + + function testSafeBatchTransferFromToEOA() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(0xBEEF), ids, transferAmounts, ""); + + assertEq(token.balanceOf(address(from), 1337), 0); + assertEq(token.balanceOf(address(0xBEEF), 1337), 1); + + assertEq(token.balanceOf(address(from), 1338), 0); + assertEq(token.balanceOf(address(0xBEEF), 1338), 1); + + assertEq(token.balanceOf(address(from), 1339), 0); + assertEq(token.balanceOf(address(0xBEEF), 1339), 1); + + assertEq(token.balanceOf(address(from), 1340), 0); + assertEq(token.balanceOf(address(0xBEEF), 1340), 1); + + assertEq(token.balanceOf(address(from), 1341), 0); + assertEq(token.balanceOf(address(0xBEEF), 1341), 1); + } + + function testSafeBatchTransferFromToERC1155Recipient() public { + ERC1155BUser from = new ERC1155BUser(token); + + ERC1155BRecipient to = new ERC1155BRecipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(to), ids, transferAmounts, "testing 123"); + + assertEq(to.batchOperator(), address(this)); + assertEq(to.batchFrom(), address(from)); + assertUintArrayEq(to.batchIds(), ids); + assertUintArrayEq(to.batchAmounts(), transferAmounts); + assertBytesEq(to.batchData(), "testing 123"); + + assertEq(token.balanceOf(address(from), 1337), 0); + assertEq(token.balanceOf(address(to), 1337), 1); + + assertEq(token.balanceOf(address(from), 1338), 0); + assertEq(token.balanceOf(address(to), 1338), 1); + + assertEq(token.balanceOf(address(from), 1339), 0); + assertEq(token.balanceOf(address(to), 1339), 1); + + assertEq(token.balanceOf(address(from), 1340), 0); + assertEq(token.balanceOf(address(to), 1340), 1); + + assertEq(token.balanceOf(address(from), 1341), 0); + assertEq(token.balanceOf(address(to), 1341), 1); + } + + function testBatchBalanceOf() public { + address[] memory tos = new address[](5); + tos[0] = address(0xBEEF); + tos[1] = address(0xCAFE); + tos[2] = address(0xFACE); + tos[3] = address(0xDEAD); + tos[4] = address(0xFEED); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.mint(address(0xBEEF), 1337, ""); + token.mint(address(0xCAFE), 1338, ""); + token.mint(address(0xFACE), 1339, ""); + token.mint(address(0xDEAD), 1340, ""); + token.mint(address(0xFEED), 1341, ""); + + uint256[] memory balances = token.balanceOfBatch(tos, ids); + + assertEq(balances[0], 1); + assertEq(balances[1], 1); + assertEq(balances[2], 1); + assertEq(balances[3], 1); + assertEq(balances[4], 1); + } + + function testFailMintToZero() public { + token.mint(address(0), 1337, ""); + } + + function testFailMintToNonERC1155Recipient() public { + token.mint(address(new NonERC1155BRecipient()), 1337, ""); + } + + function testFailMintToRevertingERC1155Recipient() public { + token.mint(address(new RevertingERC1155Recipient()), 1337, ""); + } + + function testFailMintToWrongReturnDataERC1155Recipient() public { + token.mint(address(new RevertingERC1155Recipient()), 1337, ""); + } + + function testFailBurnInsufficientBalance() public { + token.burn(1337); + } + + function testFailSafeTransferFromInsufficientBalance() public { + ERC1155BUser from = new ERC1155BUser(token); + + from.setApprovalForAll(address(this), true); + + token.safeTransferFrom(address(from), address(0xBEEF), 1337, 1, ""); + } + + function testFailSafeTransferFromSelfInsufficientBalance() public { + token.safeTransferFrom(address(this), address(0xBEEF), 1337, 1, ""); + } + + function testFailSafeTransferFromToZero() public { + token.safeTransferFrom(address(this), address(0), 1337, 1, ""); + } + + function testFailSafeTransferFromToNonERC1155Recipient() public { + token.mint(address(this), 1337, ""); + token.safeTransferFrom(address(this), address(new NonERC1155BRecipient()), 1337, 1, ""); + } + + function testFailSafeTransferFromToRevertingERC1155Recipient() public { + token.mint(address(this), 1337, ""); + token.safeTransferFrom(address(this), address(new RevertingERC1155Recipient()), 1337, 1, ""); + } + + function testFailSafeTransferFromToWrongReturnDataERC1155Recipient() public { + token.mint(address(this), 1337, ""); + token.safeTransferFrom(address(this), address(new WrongReturnDataERC1155BRecipient()), 1337, 1, ""); + } + + function testFailSafeBatchTransferInsufficientBalance() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(0xBEEF), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToZero() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(0), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToNonERC1155Recipient() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(new NonERC1155BRecipient()), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToRevertingERC1155Recipient() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(new RevertingERC1155Recipient()), ids, transferAmounts, ""); + } + + function testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](5); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + transferAmounts[4] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom( + address(from), + address(new WrongReturnDataERC1155BRecipient()), + ids, + transferAmounts, + "" + ); + } + + function testFailSafeBatchTransferFromWithArrayLengthMismatch() public { + ERC1155BUser from = new ERC1155BUser(token); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + uint256[] memory transferAmounts = new uint256[](4); + transferAmounts[0] = 1; + transferAmounts[1] = 1; + transferAmounts[2] = 1; + transferAmounts[3] = 1; + + token.batchMint(address(from), ids, ""); + + from.setApprovalForAll(address(this), true); + + token.safeBatchTransferFrom(address(from), address(0xBEEF), ids, transferAmounts, ""); + } + + function testFailBatchMintToZero() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchMint(address(0), ids, ""); + } + + function testFailBatchMintToNonERC1155Recipient() public { + NonERC1155BRecipient to = new NonERC1155BRecipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchMint(address(to), ids, ""); + } + + function testFailBatchMintToRevertingERC1155Recipient() public { + RevertingERC1155Recipient to = new RevertingERC1155Recipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchMint(address(to), ids, ""); + } + + function testFailBatchMintToWrongReturnDataERC1155Recipient() public { + WrongReturnDataERC1155BRecipient to = new WrongReturnDataERC1155BRecipient(); + + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchMint(address(to), ids, ""); + } + + function testFailBatchBurnInsufficientBalance() public { + uint256[] memory ids = new uint256[](5); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + ids[4] = 1341; + + token.batchBurn(address(0xBEEF), ids); + } + + function testFailBalanceOfBatchWithArrayMismatch() public view { + address[] memory tos = new address[](5); + tos[0] = address(0xBEEF); + tos[1] = address(0xCAFE); + tos[2] = address(0xFACE); + tos[3] = address(0xDEAD); + tos[4] = address(0xFEED); + + uint256[] memory ids = new uint256[](4); + ids[0] = 1337; + ids[1] = 1338; + ids[2] = 1339; + ids[3] = 1340; + + token.balanceOfBatch(tos, ids); + } +} diff --git a/lib/solmate/src/test/ERC20.t.sol b/lib/solmate/src/test/ERC20.t.sol new file mode 100644 index 0000000..530a8b2 --- /dev/null +++ b/lib/solmate/src/test/ERC20.t.sol @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; + +import {MockERC20} from "./utils/mocks/MockERC20.sol"; + +contract ERC20Test is DSTestPlus { + MockERC20 token; + + bytes32 constant PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + + function setUp() public { + token = new MockERC20("Token", "TKN", 18); + } + + function invariantMetadata() public { + assertEq(token.name(), "Token"); + assertEq(token.symbol(), "TKN"); + assertEq(token.decimals(), 18); + } + + function testMint() public { + token.mint(address(0xBEEF), 1e18); + + assertEq(token.totalSupply(), 1e18); + assertEq(token.balanceOf(address(0xBEEF)), 1e18); + } + + function testBurn() public { + token.mint(address(0xBEEF), 1e18); + token.burn(address(0xBEEF), 0.9e18); + + assertEq(token.totalSupply(), 1e18 - 0.9e18); + assertEq(token.balanceOf(address(0xBEEF)), 0.1e18); + } + + function testApprove() public { + assertTrue(token.approve(address(0xBEEF), 1e18)); + + assertEq(token.allowance(address(this), address(0xBEEF)), 1e18); + } + + function testTransfer() public { + token.mint(address(this), 1e18); + + assertTrue(token.transfer(address(0xBEEF), 1e18)); + assertEq(token.totalSupply(), 1e18); + + assertEq(token.balanceOf(address(this)), 0); + assertEq(token.balanceOf(address(0xBEEF)), 1e18); + } + + function testTransferFrom() public { + address from = address(0xABCD); + + token.mint(from, 1e18); + + hevm.prank(from); + token.approve(address(this), 1e18); + + assertTrue(token.transferFrom(from, address(0xBEEF), 1e18)); + assertEq(token.totalSupply(), 1e18); + + assertEq(token.allowance(from, address(this)), 0); + + assertEq(token.balanceOf(from), 0); + assertEq(token.balanceOf(address(0xBEEF)), 1e18); + } + + function testInfiniteApproveTransferFrom() public { + address from = address(0xABCD); + + token.mint(from, 1e18); + + hevm.prank(from); + token.approve(address(this), type(uint256).max); + + assertTrue(token.transferFrom(from, address(0xBEEF), 1e18)); + assertEq(token.totalSupply(), 1e18); + + assertEq(token.allowance(from, address(this)), type(uint256).max); + + assertEq(token.balanceOf(from), 0); + assertEq(token.balanceOf(address(0xBEEF)), 1e18); + } + + function testPermit() public { + uint256 privateKey = 0xBEEF; + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp)) + ) + ) + ); + + token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); + + assertEq(token.allowance(owner, address(0xCAFE)), 1e18); + assertEq(token.nonces(owner), 1); + } + + function testFailTransferInsufficientBalance() public { + token.mint(address(this), 0.9e18); + token.transfer(address(0xBEEF), 1e18); + } + + function testFailTransferFromInsufficientAllowance() public { + address from = address(0xABCD); + + token.mint(from, 1e18); + + hevm.prank(from); + token.approve(address(this), 0.9e18); + + token.transferFrom(from, address(0xBEEF), 1e18); + } + + function testFailTransferFromInsufficientBalance() public { + address from = address(0xABCD); + + token.mint(from, 0.9e18); + + hevm.prank(from); + token.approve(address(this), 1e18); + + token.transferFrom(from, address(0xBEEF), 1e18); + } + + function testFailPermitBadNonce() public { + uint256 privateKey = 0xBEEF; + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 1, block.timestamp)) + ) + ) + ); + + token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); + } + + function testFailPermitBadDeadline() public { + uint256 privateKey = 0xBEEF; + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp)) + ) + ) + ); + + token.permit(owner, address(0xCAFE), 1e18, block.timestamp + 1, v, r, s); + } + + function testFailPermitPastDeadline() public { + uint256 privateKey = 0xBEEF; + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp - 1)) + ) + ) + ); + + token.permit(owner, address(0xCAFE), 1e18, block.timestamp - 1, v, r, s); + } + + function testFailPermitReplay() public { + uint256 privateKey = 0xBEEF; + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp)) + ) + ) + ); + + token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); + token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); + } + + function testFuzzMetadata( + string calldata name, + string calldata symbol, + uint8 decimals + ) public { + MockERC20 tkn = new MockERC20(name, symbol, decimals); + assertEq(tkn.name(), name); + assertEq(tkn.symbol(), symbol); + assertEq(tkn.decimals(), decimals); + } + + function testFuzzMint(address from, uint256 amount) public { + token.mint(from, amount); + + assertEq(token.totalSupply(), amount); + assertEq(token.balanceOf(from), amount); + } + + function testFuzzBurn( + address from, + uint256 mintAmount, + uint256 burnAmount + ) public { + burnAmount = bound(burnAmount, 0, mintAmount); + + token.mint(from, mintAmount); + token.burn(from, burnAmount); + + assertEq(token.totalSupply(), mintAmount - burnAmount); + assertEq(token.balanceOf(from), mintAmount - burnAmount); + } + + function testFuzzApprove(address to, uint256 amount) public { + assertTrue(token.approve(to, amount)); + + assertEq(token.allowance(address(this), to), amount); + } + + function testFuzzTransfer(address from, uint256 amount) public { + token.mint(address(this), amount); + + assertTrue(token.transfer(from, amount)); + assertEq(token.totalSupply(), amount); + + if (address(this) == from) { + assertEq(token.balanceOf(address(this)), amount); + } else { + assertEq(token.balanceOf(address(this)), 0); + assertEq(token.balanceOf(from), amount); + } + } + + function testFuzzTransferFrom( + address to, + uint256 approval, + uint256 amount + ) public { + amount = bound(amount, 0, approval); + + address from = address(0xABCD); + + token.mint(from, amount); + + hevm.prank(from); + token.approve(address(this), approval); + + assertTrue(token.transferFrom(from, to, amount)); + assertEq(token.totalSupply(), amount); + + uint256 app = from == address(this) || approval == type(uint256).max ? approval : approval - amount; + assertEq(token.allowance(from, address(this)), app); + + if (from == to) { + assertEq(token.balanceOf(from), amount); + } else { + assertEq(token.balanceOf(from), 0); + assertEq(token.balanceOf(to), amount); + } + } + + function testFuzzPermit( + uint248 privKey, + address to, + uint256 amount, + uint256 deadline + ) public { + uint256 privateKey = privKey; + if (deadline < block.timestamp) deadline = block.timestamp; + if (privateKey == 0) privateKey = 1; + + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) + ) + ) + ); + + token.permit(owner, to, amount, deadline, v, r, s); + + assertEq(token.allowance(owner, to), amount); + assertEq(token.nonces(owner), 1); + } + + function testFailFuzzBurnInsufficientBalance( + address to, + uint256 mintAmount, + uint256 burnAmount + ) public { + burnAmount = bound(burnAmount, mintAmount + 1, type(uint256).max); + + token.mint(to, mintAmount); + token.burn(to, burnAmount); + } + + function testFailFuzzTransferInsufficientBalance( + address to, + uint256 mintAmount, + uint256 sendAmount + ) public { + sendAmount = bound(sendAmount, mintAmount + 1, type(uint256).max); + + token.mint(address(this), mintAmount); + token.transfer(to, sendAmount); + } + + function testFailFuzzTransferFromInsufficientAllowance( + address to, + uint256 approval, + uint256 amount + ) public { + amount = bound(amount, approval + 1, type(uint256).max); + + address from = address(0xABCD); + + token.mint(from, amount); + + hevm.prank(from); + token.approve(address(this), approval); + + token.transferFrom(from, to, amount); + } + + function testFailFuzzTransferFromInsufficientBalance( + address to, + uint256 mintAmount, + uint256 sendAmount + ) public { + sendAmount = bound(sendAmount, mintAmount + 1, type(uint256).max); + + address from = address(0xABCD); + + token.mint(from, mintAmount); + + hevm.prank(from); + token.approve(address(this), sendAmount); + + token.transferFrom(from, to, sendAmount); + } + + function testFailFuzzPermitBadNonce( + uint256 privateKey, + address to, + uint256 amount, + uint256 deadline, + uint256 nonce + ) public { + if (deadline < block.timestamp) deadline = block.timestamp; + if (privateKey == 0) privateKey = 1; + if (nonce == 0) nonce = 1; + + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, nonce, deadline)) + ) + ) + ); + + token.permit(owner, to, amount, deadline, v, r, s); + } + + function testFailFuzzPermitBadDeadline( + uint256 privateKey, + address to, + uint256 amount, + uint256 deadline + ) public { + if (deadline < block.timestamp) deadline = block.timestamp; + if (privateKey == 0) privateKey = 1; + + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) + ) + ) + ); + + token.permit(owner, to, amount, deadline + 1, v, r, s); + } + + function testFailFuzzPermitPastDeadline( + uint256 privateKey, + address to, + uint256 amount, + uint256 deadline + ) public { + deadline = bound(deadline, 0, block.timestamp - 1); + if (privateKey == 0) privateKey = 1; + + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) + ) + ) + ); + + token.permit(owner, to, amount, deadline, v, r, s); + } + + function testFailFuzzPermitReplay( + uint256 privateKey, + address to, + uint256 amount, + uint256 deadline + ) public { + if (deadline < block.timestamp) deadline = block.timestamp; + if (privateKey == 0) privateKey = 1; + + address owner = hevm.addr(privateKey); + + (uint8 v, bytes32 r, bytes32 s) = hevm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + token.DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) + ) + ) + ); + + token.permit(owner, to, amount, deadline, v, r, s); + token.permit(owner, to, amount, deadline, v, r, s); + } +} + +contract ERC20Invariants is DSTestPlus, DSInvariantTest { + BalanceSum balanceSum; + MockERC20 token; + + function setUp() public { + token = new MockERC20("Token", "TKN", 18); + balanceSum = new BalanceSum(token); + + addTargetContract(address(balanceSum)); + } + + function invariantBalanceSum() public { + assertEq(token.totalSupply(), balanceSum.sum()); + } +} + +contract BalanceSum { + MockERC20 token; + uint256 public sum; + + constructor(MockERC20 _token) { + token = _token; + } + + function mint(address from, uint256 amount) public { + token.mint(from, amount); + sum += amount; + } + + function burn(address from, uint256 amount) public { + token.burn(from, amount); + sum -= amount; + } + + function approve(address to, uint256 amount) public { + token.approve(to, amount); + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public { + token.transferFrom(from, to, amount); + } + + function transfer(address to, uint256 amount) public { + token.transfer(to, amount); + } +} diff --git a/lib/solmate/src/test/ERC4626.t.sol b/lib/solmate/src/test/ERC4626.t.sol new file mode 100644 index 0000000..3c143a9 --- /dev/null +++ b/lib/solmate/src/test/ERC4626.t.sol @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {MockERC20} from "./utils/mocks/MockERC20.sol"; +import {MockERC4626} from "./utils/mocks/MockERC4626.sol"; + +contract ERC4626Test is DSTestPlus { + MockERC20 underlying; + MockERC4626 vault; + + function setUp() public { + underlying = new MockERC20("Mock Token", "TKN", 18); + vault = new MockERC4626(underlying, "Mock Token Vault", "vwTKN"); + } + + function invariantMetadata() public { + assertEq(vault.name(), "Mock Token Vault"); + assertEq(vault.symbol(), "vwTKN"); + assertEq(vault.decimals(), 18); + } + + function testFuzzMetadata(string calldata name, string calldata symbol) public { + MockERC4626 vlt = new MockERC4626(underlying, name, symbol); + assertEq(vlt.name(), name); + assertEq(vlt.symbol(), symbol); + assertEq(address(vlt.asset()), address(underlying)); + } + + function testFuzzSingleDepositWithdraw(uint128 amount) public { + if (amount == 0) amount = 1; + + uint256 aliceUnderlyingAmount = amount; + + address alice = address(0xABCD); + + underlying.mint(alice, aliceUnderlyingAmount); + + hevm.prank(alice); + underlying.approve(address(vault), aliceUnderlyingAmount); + assertEq(underlying.allowance(alice, address(vault)), aliceUnderlyingAmount); + + uint256 alicePreDepositBal = underlying.balanceOf(alice); + + hevm.prank(alice); + uint256 aliceShareAmount = vault.deposit(aliceUnderlyingAmount, alice); + + assertEq(vault.afterDepositHookCalledCounter(), 1); + + // Expect exchange rate to be 1:1 on initial deposit. + assertEq(aliceUnderlyingAmount, aliceShareAmount); + assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); + assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); + assertEq(vault.totalSupply(), aliceShareAmount); + assertEq(vault.totalAssets(), aliceUnderlyingAmount); + assertEq(vault.balanceOf(alice), aliceShareAmount); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); + assertEq(underlying.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); + + hevm.prank(alice); + vault.withdraw(aliceUnderlyingAmount, alice, alice); + + assertEq(vault.beforeWithdrawHookCalledCounter(), 1); + + assertEq(vault.totalAssets(), 0); + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); + assertEq(underlying.balanceOf(alice), alicePreDepositBal); + } + + function testFuzzSingleMintRedeem(uint128 amount) public { + if (amount == 0) amount = 1; + + uint256 aliceShareAmount = amount; + + address alice = address(0xABCD); + + underlying.mint(alice, aliceShareAmount); + + hevm.prank(alice); + underlying.approve(address(vault), aliceShareAmount); + assertEq(underlying.allowance(alice, address(vault)), aliceShareAmount); + + uint256 alicePreDepositBal = underlying.balanceOf(alice); + + hevm.prank(alice); + uint256 aliceUnderlyingAmount = vault.mint(aliceShareAmount, alice); + + assertEq(vault.afterDepositHookCalledCounter(), 1); + + // Expect exchange rate to be 1:1 on initial mint. + assertEq(aliceShareAmount, aliceUnderlyingAmount); + assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); + assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); + assertEq(vault.totalSupply(), aliceShareAmount); + assertEq(vault.totalAssets(), aliceUnderlyingAmount); + assertEq(vault.balanceOf(alice), aliceUnderlyingAmount); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); + assertEq(underlying.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); + + hevm.prank(alice); + vault.redeem(aliceShareAmount, alice, alice); + + assertEq(vault.beforeWithdrawHookCalledCounter(), 1); + + assertEq(vault.totalAssets(), 0); + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); + assertEq(underlying.balanceOf(alice), alicePreDepositBal); + } + + function testMultipleMintDepositRedeemWithdraw() public { + // Scenario: + // A = Alice, B = Bob + // ________________________________________________________ + // | Vault shares | A share | A assets | B share | B assets | + // |========================================================| + // | 1. Alice mints 2000 shares (costs 2000 tokens) | + // |--------------|---------|----------|---------|----------| + // | 2000 | 2000 | 2000 | 0 | 0 | + // |--------------|---------|----------|---------|----------| + // | 2. Bob deposits 4000 tokens (mints 4000 shares) | + // |--------------|---------|----------|---------|----------| + // | 6000 | 2000 | 2000 | 4000 | 4000 | + // |--------------|---------|----------|---------|----------| + // | 3. Vault mutates by +3000 tokens... | + // | (simulated yield returned from strategy)... | + // |--------------|---------|----------|---------|----------| + // | 6000 | 2000 | 3000 | 4000 | 6000 | + // |--------------|---------|----------|---------|----------| + // | 4. Alice deposits 2000 tokens (mints 1333 shares) | + // |--------------|---------|----------|---------|----------| + // | 7333 | 3333 | 4999 | 4000 | 6000 | + // |--------------|---------|----------|---------|----------| + // | 5. Bob mints 2000 shares (costs 3001 assets) | + // | NOTE: Bob's assets spent got rounded up | + // | NOTE: Alice's vault assets got rounded up | + // |--------------|---------|----------|---------|----------| + // | 9333 | 3333 | 5000 | 6000 | 9000 | + // |--------------|---------|----------|---------|----------| + // | 6. Vault mutates by +3000 tokens... | + // | (simulated yield returned from strategy) | + // | NOTE: Vault holds 17001 tokens, but sum of | + // | assetsOf() is 17000. | + // |--------------|---------|----------|---------|----------| + // | 9333 | 3333 | 6071 | 6000 | 10929 | + // |--------------|---------|----------|---------|----------| + // | 7. Alice redeem 1333 shares (2428 assets) | + // |--------------|---------|----------|---------|----------| + // | 8000 | 2000 | 3643 | 6000 | 10929 | + // |--------------|---------|----------|---------|----------| + // | 8. Bob withdraws 2928 assets (1608 shares) | + // |--------------|---------|----------|---------|----------| + // | 6392 | 2000 | 3643 | 4392 | 8000 | + // |--------------|---------|----------|---------|----------| + // | 9. Alice withdraws 3643 assets (2000 shares) | + // | NOTE: Bob's assets have been rounded back up | + // |--------------|---------|----------|---------|----------| + // | 4392 | 0 | 0 | 4392 | 8001 | + // |--------------|---------|----------|---------|----------| + // | 10. Bob redeem 4392 shares (8001 tokens) | + // |--------------|---------|----------|---------|----------| + // | 0 | 0 | 0 | 0 | 0 | + // |______________|_________|__________|_________|__________| + + address alice = address(0xABCD); + address bob = address(0xDCBA); + + uint256 mutationUnderlyingAmount = 3000; + + underlying.mint(alice, 4000); + + hevm.prank(alice); + underlying.approve(address(vault), 4000); + + assertEq(underlying.allowance(alice, address(vault)), 4000); + + underlying.mint(bob, 7001); + + hevm.prank(bob); + underlying.approve(address(vault), 7001); + + assertEq(underlying.allowance(bob, address(vault)), 7001); + + // 1. Alice mints 2000 shares (costs 2000 tokens) + hevm.prank(alice); + uint256 aliceUnderlyingAmount = vault.mint(2000, alice); + + uint256 aliceShareAmount = vault.previewDeposit(aliceUnderlyingAmount); + assertEq(vault.afterDepositHookCalledCounter(), 1); + + // Expect to have received the requested mint amount. + assertEq(aliceShareAmount, 2000); + assertEq(vault.balanceOf(alice), aliceShareAmount); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); + assertEq(vault.convertToShares(aliceUnderlyingAmount), vault.balanceOf(alice)); + + // Expect a 1:1 ratio before mutation. + assertEq(aliceUnderlyingAmount, 2000); + + // Sanity check. + assertEq(vault.totalSupply(), aliceShareAmount); + assertEq(vault.totalAssets(), aliceUnderlyingAmount); + + // 2. Bob deposits 4000 tokens (mints 4000 shares) + hevm.prank(bob); + uint256 bobShareAmount = vault.deposit(4000, bob); + uint256 bobUnderlyingAmount = vault.previewWithdraw(bobShareAmount); + assertEq(vault.afterDepositHookCalledCounter(), 2); + + // Expect to have received the requested underlying amount. + assertEq(bobUnderlyingAmount, 4000); + assertEq(vault.balanceOf(bob), bobShareAmount); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), bobUnderlyingAmount); + assertEq(vault.convertToShares(bobUnderlyingAmount), vault.balanceOf(bob)); + + // Expect a 1:1 ratio before mutation. + assertEq(bobShareAmount, bobUnderlyingAmount); + + // Sanity check. + uint256 preMutationShareBal = aliceShareAmount + bobShareAmount; + uint256 preMutationBal = aliceUnderlyingAmount + bobUnderlyingAmount; + assertEq(vault.totalSupply(), preMutationShareBal); + assertEq(vault.totalAssets(), preMutationBal); + assertEq(vault.totalSupply(), 6000); + assertEq(vault.totalAssets(), 6000); + + // 3. Vault mutates by +3000 tokens... | + // (simulated yield returned from strategy)... + // The Vault now contains more tokens than deposited which causes the exchange rate to change. + // Alice share is 33.33% of the Vault, Bob 66.66% of the Vault. + // Alice's share count stays the same but the underlying amount changes from 2000 to 3000. + // Bob's share count stays the same but the underlying amount changes from 4000 to 6000. + underlying.mint(address(vault), mutationUnderlyingAmount); + assertEq(vault.totalSupply(), preMutationShareBal); + assertEq(vault.totalAssets(), preMutationBal + mutationUnderlyingAmount); + assertEq(vault.balanceOf(alice), aliceShareAmount); + assertEq( + vault.convertToAssets(vault.balanceOf(alice)), + aliceUnderlyingAmount + (mutationUnderlyingAmount / 3) * 1 + ); + assertEq(vault.balanceOf(bob), bobShareAmount); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), bobUnderlyingAmount + (mutationUnderlyingAmount / 3) * 2); + + // 4. Alice deposits 2000 tokens (mints 1333 shares) + hevm.prank(alice); + vault.deposit(2000, alice); + + assertEq(vault.totalSupply(), 7333); + assertEq(vault.balanceOf(alice), 3333); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 4999); + assertEq(vault.balanceOf(bob), 4000); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 6000); + + // 5. Bob mints 2000 shares (costs 3001 assets) + // NOTE: Bob's assets spent got rounded up + // NOTE: Alices's vault assets got rounded up + hevm.prank(bob); + vault.mint(2000, bob); + + assertEq(vault.totalSupply(), 9333); + assertEq(vault.balanceOf(alice), 3333); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 5000); + assertEq(vault.balanceOf(bob), 6000); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 9000); + + // Sanity checks: + // Alice and bob should have spent all their tokens now + assertEq(underlying.balanceOf(alice), 0); + assertEq(underlying.balanceOf(bob), 0); + // Assets in vault: 4k (alice) + 7k (bob) + 3k (yield) + 1 (round up) + assertEq(vault.totalAssets(), 14001); + + // 6. Vault mutates by +3000 tokens + // NOTE: Vault holds 17001 tokens, but sum of assetsOf() is 17000. + underlying.mint(address(vault), mutationUnderlyingAmount); + assertEq(vault.totalAssets(), 17001); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 6071); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 10929); + + // 7. Alice redeem 1333 shares (2428 assets) + hevm.prank(alice); + vault.redeem(1333, alice, alice); + + assertEq(underlying.balanceOf(alice), 2428); + assertEq(vault.totalSupply(), 8000); + assertEq(vault.totalAssets(), 14573); + assertEq(vault.balanceOf(alice), 2000); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 3643); + assertEq(vault.balanceOf(bob), 6000); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 10929); + + // 8. Bob withdraws 2929 assets (1608 shares) + hevm.prank(bob); + vault.withdraw(2929, bob, bob); + + assertEq(underlying.balanceOf(bob), 2929); + assertEq(vault.totalSupply(), 6392); + assertEq(vault.totalAssets(), 11644); + assertEq(vault.balanceOf(alice), 2000); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 3643); + assertEq(vault.balanceOf(bob), 4392); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 8000); + + // 9. Alice withdraws 3643 assets (2000 shares) + // NOTE: Bob's assets have been rounded back up + hevm.prank(alice); + vault.withdraw(3643, alice, alice); + + assertEq(underlying.balanceOf(alice), 6071); + assertEq(vault.totalSupply(), 4392); + assertEq(vault.totalAssets(), 8001); + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); + assertEq(vault.balanceOf(bob), 4392); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 8001); + + // 10. Bob redeem 4392 shares (8001 tokens) + hevm.prank(bob); + vault.redeem(4392, bob, bob); + assertEq(underlying.balanceOf(bob), 10930); + assertEq(vault.totalSupply(), 0); + assertEq(vault.totalAssets(), 0); + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); + assertEq(vault.balanceOf(bob), 0); + assertEq(vault.convertToAssets(vault.balanceOf(bob)), 0); + + // Sanity check + assertEq(underlying.balanceOf(address(vault)), 0); + } + + function testFailDepositWithNotEnoughApproval() public { + underlying.mint(address(this), 0.5e18); + underlying.approve(address(vault), 0.5e18); + assertEq(underlying.allowance(address(this), address(vault)), 0.5e18); + + vault.deposit(1e18, address(this)); + } + + function testFailWithdrawWithNotEnoughUnderlyingAmount() public { + underlying.mint(address(this), 0.5e18); + underlying.approve(address(vault), 0.5e18); + + vault.deposit(0.5e18, address(this)); + + vault.withdraw(1e18, address(this), address(this)); + } + + function testFailRedeemWithNotEnoughShareAmount() public { + underlying.mint(address(this), 0.5e18); + underlying.approve(address(vault), 0.5e18); + + vault.deposit(0.5e18, address(this)); + + vault.redeem(1e18, address(this), address(this)); + } + + function testFailWithdrawWithNoUnderlyingAmount() public { + vault.withdraw(1e18, address(this), address(this)); + } + + function testFailRedeemWithNoShareAmount() public { + vault.redeem(1e18, address(this), address(this)); + } + + function testFailDepositWithNoApproval() public { + vault.deposit(1e18, address(this)); + } + + function testFailMintWithNoApproval() public { + vault.mint(1e18, address(this)); + } + + function testFailDepositZero() public { + vault.deposit(0, address(this)); + } + + function testMintZero() public { + vault.mint(0, address(this)); + + assertEq(vault.balanceOf(address(this)), 0); + assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); + assertEq(vault.totalSupply(), 0); + assertEq(vault.totalAssets(), 0); + } + + function testFailRedeemZero() public { + vault.redeem(0, address(this), address(this)); + } + + function testWithdrawZero() public { + vault.withdraw(0, address(this), address(this)); + + assertEq(vault.balanceOf(address(this)), 0); + assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); + assertEq(vault.totalSupply(), 0); + assertEq(vault.totalAssets(), 0); + } + + function testVaultInteractionsForSomeoneElse() public { + // init 2 users with a 1e18 balance + address alice = address(0xABCD); + address bob = address(0xDCBA); + underlying.mint(alice, 1e18); + underlying.mint(bob, 1e18); + + hevm.prank(alice); + underlying.approve(address(vault), 1e18); + + hevm.prank(bob); + underlying.approve(address(vault), 1e18); + + // alice deposits 1e18 for bob + hevm.prank(alice); + vault.deposit(1e18, bob); + + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.balanceOf(bob), 1e18); + assertEq(underlying.balanceOf(alice), 0); + + // bob mint 1e18 for alice + hevm.prank(bob); + vault.mint(1e18, alice); + assertEq(vault.balanceOf(alice), 1e18); + assertEq(vault.balanceOf(bob), 1e18); + assertEq(underlying.balanceOf(bob), 0); + + // alice redeem 1e18 for bob + hevm.prank(alice); + vault.redeem(1e18, bob, alice); + + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.balanceOf(bob), 1e18); + assertEq(underlying.balanceOf(bob), 1e18); + + // bob withdraw 1e18 for alice + hevm.prank(bob); + vault.withdraw(1e18, alice, bob); + + assertEq(vault.balanceOf(alice), 0); + assertEq(vault.balanceOf(bob), 0); + assertEq(underlying.balanceOf(alice), 1e18); + } +} diff --git a/lib/solmate/src/test/ERC721.t.sol b/lib/solmate/src/test/ERC721.t.sol new file mode 100644 index 0000000..d0d87a6 --- /dev/null +++ b/lib/solmate/src/test/ERC721.t.sol @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; + +import {MockERC721} from "./utils/mocks/MockERC721.sol"; + +import {ERC721TokenReceiver} from "../tokens/ERC721.sol"; + +contract ERC721Recipient is ERC721TokenReceiver { + address public operator; + address public from; + uint256 public id; + bytes public data; + + function onERC721Received( + address _operator, + address _from, + uint256 _id, + bytes calldata _data + ) public virtual override returns (bytes4) { + operator = _operator; + from = _from; + id = _id; + data = _data; + + return ERC721TokenReceiver.onERC721Received.selector; + } +} + +contract RevertingERC721Recipient is ERC721TokenReceiver { + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) public virtual override returns (bytes4) { + revert(string(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector))); + } +} + +contract WrongReturnDataERC721Recipient is ERC721TokenReceiver { + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) public virtual override returns (bytes4) { + return 0xCAFEBEEF; + } +} + +contract NonERC721Recipient {} + +contract ERC721Test is DSTestPlus { + MockERC721 token; + + function setUp() public { + token = new MockERC721("Token", "TKN"); + } + + function invariantMetadata() public { + assertEq(token.name(), "Token"); + assertEq(token.symbol(), "TKN"); + } + + function testMint() public { + token.mint(address(0xBEEF), 1337); + + assertEq(token.balanceOf(address(0xBEEF)), 1); + assertEq(token.ownerOf(1337), address(0xBEEF)); + } + + function testBurn() public { + token.mint(address(0xBEEF), 1337); + token.burn(1337); + + assertEq(token.balanceOf(address(0xBEEF)), 0); + + hevm.expectRevert("NOT_MINTED"); + token.ownerOf(1337); + } + + function testApprove() public { + token.mint(address(this), 1337); + + token.approve(address(0xBEEF), 1337); + + assertEq(token.getApproved(1337), address(0xBEEF)); + } + + function testApproveBurn() public { + token.mint(address(this), 1337); + + token.approve(address(0xBEEF), 1337); + + token.burn(1337); + + assertEq(token.balanceOf(address(this)), 0); + assertEq(token.getApproved(1337), address(0)); + + hevm.expectRevert("NOT_MINTED"); + token.ownerOf(1337); + } + + function testApproveAll() public { + token.setApprovalForAll(address(0xBEEF), true); + + assertTrue(token.isApprovedForAll(address(this), address(0xBEEF))); + } + + function testTransferFrom() public { + address from = address(0xABCD); + + token.mint(from, 1337); + + hevm.prank(from); + token.approve(address(this), 1337); + + token.transferFrom(from, address(0xBEEF), 1337); + + assertEq(token.getApproved(1337), address(0)); + assertEq(token.ownerOf(1337), address(0xBEEF)); + assertEq(token.balanceOf(address(0xBEEF)), 1); + assertEq(token.balanceOf(from), 0); + } + + function testTransferFromSelf() public { + token.mint(address(this), 1337); + + token.transferFrom(address(this), address(0xBEEF), 1337); + + assertEq(token.getApproved(1337), address(0)); + assertEq(token.ownerOf(1337), address(0xBEEF)); + assertEq(token.balanceOf(address(0xBEEF)), 1); + assertEq(token.balanceOf(address(this)), 0); + } + + function testTransferFromApproveAll() public { + address from = address(0xABCD); + + token.mint(from, 1337); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.transferFrom(from, address(0xBEEF), 1337); + + assertEq(token.getApproved(1337), address(0)); + assertEq(token.ownerOf(1337), address(0xBEEF)); + assertEq(token.balanceOf(address(0xBEEF)), 1); + assertEq(token.balanceOf(from), 0); + } + + function testSafeTransferFromToEOA() public { + address from = address(0xABCD); + + token.mint(from, 1337); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(0xBEEF), 1337); + + assertEq(token.getApproved(1337), address(0)); + assertEq(token.ownerOf(1337), address(0xBEEF)); + assertEq(token.balanceOf(address(0xBEEF)), 1); + assertEq(token.balanceOf(from), 0); + } + + function testSafeTransferFromToERC721Recipient() public { + address from = address(0xABCD); + ERC721Recipient recipient = new ERC721Recipient(); + + token.mint(from, 1337); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(recipient), 1337); + + assertEq(token.getApproved(1337), address(0)); + assertEq(token.ownerOf(1337), address(recipient)); + assertEq(token.balanceOf(address(recipient)), 1); + assertEq(token.balanceOf(from), 0); + + assertEq(recipient.operator(), address(this)); + assertEq(recipient.from(), from); + assertEq(recipient.id(), 1337); + assertBytesEq(recipient.data(), ""); + } + + function testSafeTransferFromToERC721RecipientWithData() public { + address from = address(0xABCD); + ERC721Recipient recipient = new ERC721Recipient(); + + token.mint(from, 1337); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(recipient), 1337, "testing 123"); + + assertEq(token.getApproved(1337), address(0)); + assertEq(token.ownerOf(1337), address(recipient)); + assertEq(token.balanceOf(address(recipient)), 1); + assertEq(token.balanceOf(from), 0); + + assertEq(recipient.operator(), address(this)); + assertEq(recipient.from(), from); + assertEq(recipient.id(), 1337); + assertBytesEq(recipient.data(), "testing 123"); + } + + function testSafeMintToEOA() public { + token.safeMint(address(0xBEEF), 1337); + + assertEq(token.ownerOf(1337), address(address(0xBEEF))); + assertEq(token.balanceOf(address(address(0xBEEF))), 1); + } + + function testSafeMintToERC721Recipient() public { + ERC721Recipient to = new ERC721Recipient(); + + token.safeMint(address(to), 1337); + + assertEq(token.ownerOf(1337), address(to)); + assertEq(token.balanceOf(address(to)), 1); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), 1337); + assertBytesEq(to.data(), ""); + } + + function testSafeMintToERC721RecipientWithData() public { + ERC721Recipient to = new ERC721Recipient(); + + token.safeMint(address(to), 1337, "testing 123"); + + assertEq(token.ownerOf(1337), address(to)); + assertEq(token.balanceOf(address(to)), 1); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), 1337); + assertBytesEq(to.data(), "testing 123"); + } + + function testFailMintToZero() public { + token.mint(address(0), 1337); + } + + function testFailDoubleMint() public { + token.mint(address(0xBEEF), 1337); + token.mint(address(0xBEEF), 1337); + } + + function testFailBurnUnMinted() public { + token.burn(1337); + } + + function testFailDoubleBurn() public { + token.mint(address(0xBEEF), 1337); + + token.burn(1337); + token.burn(1337); + } + + function testFailApproveUnMinted() public { + token.approve(address(0xBEEF), 1337); + } + + function testFailApproveUnAuthorized() public { + token.mint(address(0xCAFE), 1337); + + token.approve(address(0xBEEF), 1337); + } + + function testFailTransferFromUnOwned() public { + token.transferFrom(address(0xFEED), address(0xBEEF), 1337); + } + + function testFailTransferFromWrongFrom() public { + token.mint(address(0xCAFE), 1337); + + token.transferFrom(address(0xFEED), address(0xBEEF), 1337); + } + + function testFailTransferFromToZero() public { + token.mint(address(this), 1337); + + token.transferFrom(address(this), address(0), 1337); + } + + function testFailTransferFromNotOwner() public { + token.mint(address(0xFEED), 1337); + + token.transferFrom(address(0xFEED), address(0xBEEF), 1337); + } + + function testFailSafeTransferFromToNonERC721Recipient() public { + token.mint(address(this), 1337); + + token.safeTransferFrom(address(this), address(new NonERC721Recipient()), 1337); + } + + function testFailSafeTransferFromToNonERC721RecipientWithData() public { + token.mint(address(this), 1337); + + token.safeTransferFrom(address(this), address(new NonERC721Recipient()), 1337, "testing 123"); + } + + function testFailSafeTransferFromToRevertingERC721Recipient() public { + token.mint(address(this), 1337); + + token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), 1337); + } + + function testFailSafeTransferFromToRevertingERC721RecipientWithData() public { + token.mint(address(this), 1337); + + token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), 1337, "testing 123"); + } + + function testFailSafeTransferFromToERC721RecipientWithWrongReturnData() public { + token.mint(address(this), 1337); + + token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), 1337); + } + + function testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData() public { + token.mint(address(this), 1337); + + token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), 1337, "testing 123"); + } + + function testFailSafeMintToNonERC721Recipient() public { + token.safeMint(address(new NonERC721Recipient()), 1337); + } + + function testFailSafeMintToNonERC721RecipientWithData() public { + token.safeMint(address(new NonERC721Recipient()), 1337, "testing 123"); + } + + function testFailSafeMintToRevertingERC721Recipient() public { + token.safeMint(address(new RevertingERC721Recipient()), 1337); + } + + function testFailSafeMintToRevertingERC721RecipientWithData() public { + token.safeMint(address(new RevertingERC721Recipient()), 1337, "testing 123"); + } + + function testFailSafeMintToERC721RecipientWithWrongReturnData() public { + token.safeMint(address(new WrongReturnDataERC721Recipient()), 1337); + } + + function testFailSafeMintToERC721RecipientWithWrongReturnDataWithData() public { + token.safeMint(address(new WrongReturnDataERC721Recipient()), 1337, "testing 123"); + } + + function testFailBalanceOfZeroAddress() public view { + token.balanceOf(address(0)); + } + + function testFailOwnerOfUnminted() public view { + token.ownerOf(1337); + } + + function testFuzzMetadata(string memory name, string memory symbol) public { + MockERC721 tkn = new MockERC721(name, symbol); + + assertEq(tkn.name(), name); + assertEq(tkn.symbol(), symbol); + } + + function testFuzzMint(address to, uint256 id) public { + if (to == address(0)) to = address(0xBEEF); + + token.mint(to, id); + + assertEq(token.balanceOf(to), 1); + assertEq(token.ownerOf(id), to); + } + + function testFuzzBurn(address to, uint256 id) public { + if (to == address(0)) to = address(0xBEEF); + + token.mint(to, id); + token.burn(id); + + assertEq(token.balanceOf(to), 0); + + hevm.expectRevert("NOT_MINTED"); + token.ownerOf(id); + } + + function testFuzzApprove(address to, uint256 id) public { + if (to == address(0)) to = address(0xBEEF); + + token.mint(address(this), id); + + token.approve(to, id); + + assertEq(token.getApproved(id), to); + } + + function testFuzzApproveBurn(address to, uint256 id) public { + token.mint(address(this), id); + + token.approve(address(to), id); + + token.burn(id); + + assertEq(token.balanceOf(address(this)), 0); + assertEq(token.getApproved(id), address(0)); + + hevm.expectRevert("NOT_MINTED"); + token.ownerOf(id); + } + + function testFuzzApproveAll(address to, bool approved) public { + token.setApprovalForAll(to, approved); + + assertBoolEq(token.isApprovedForAll(address(this), to), approved); + } + + function testFuzzTransferFrom(uint256 id, address to) public { + address from = address(0xABCD); + + if (to == address(0) || to == from) to = address(0xBEEF); + + token.mint(from, id); + + hevm.prank(from); + token.approve(address(this), id); + + token.transferFrom(from, to, id); + + assertEq(token.getApproved(id), address(0)); + assertEq(token.ownerOf(id), to); + assertEq(token.balanceOf(to), 1); + assertEq(token.balanceOf(from), 0); + } + + function testFuzzTransferFromSelf(uint256 id, address to) public { + if (to == address(0) || to == address(this)) to = address(0xBEEF); + + token.mint(address(this), id); + + token.transferFrom(address(this), to, id); + + assertEq(token.getApproved(id), address(0)); + assertEq(token.ownerOf(id), to); + assertEq(token.balanceOf(to), 1); + assertEq(token.balanceOf(address(this)), 0); + } + + function testFuzzTransferFromApproveAll(uint256 id, address to) public { + address from = address(0xABCD); + + if (to == address(0) || to == from) to = address(0xBEEF); + + token.mint(from, id); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.transferFrom(from, to, id); + + assertEq(token.getApproved(id), address(0)); + assertEq(token.ownerOf(id), to); + assertEq(token.balanceOf(to), 1); + assertEq(token.balanceOf(from), 0); + } + + function testFuzzSafeTransferFromToEOA(uint256 id, address to) public { + address from = address(0xABCD); + + if (to == address(0) || to == from) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + token.mint(from, id); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, to, id); + + assertEq(token.getApproved(id), address(0)); + assertEq(token.ownerOf(id), to); + assertEq(token.balanceOf(to), 1); + assertEq(token.balanceOf(from), 0); + } + + function testFuzzSafeTransferFromToERC721Recipient(uint256 id) public { + address from = address(0xABCD); + + ERC721Recipient recipient = new ERC721Recipient(); + + token.mint(from, id); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(recipient), id); + + assertEq(token.getApproved(id), address(0)); + assertEq(token.ownerOf(id), address(recipient)); + assertEq(token.balanceOf(address(recipient)), 1); + assertEq(token.balanceOf(from), 0); + + assertEq(recipient.operator(), address(this)); + assertEq(recipient.from(), from); + assertEq(recipient.id(), id); + assertBytesEq(recipient.data(), ""); + } + + function testFuzzSafeTransferFromToERC721RecipientWithData(uint256 id, bytes calldata data) public { + address from = address(0xABCD); + ERC721Recipient recipient = new ERC721Recipient(); + + token.mint(from, id); + + hevm.prank(from); + token.setApprovalForAll(address(this), true); + + token.safeTransferFrom(from, address(recipient), id, data); + + assertEq(token.getApproved(id), address(0)); + assertEq(token.ownerOf(id), address(recipient)); + assertEq(token.balanceOf(address(recipient)), 1); + assertEq(token.balanceOf(from), 0); + + assertEq(recipient.operator(), address(this)); + assertEq(recipient.from(), from); + assertEq(recipient.id(), id); + assertBytesEq(recipient.data(), data); + } + + function testFuzzSafeMintToEOA(uint256 id, address to) public { + if (to == address(0)) to = address(0xBEEF); + + if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; + + token.safeMint(to, id); + + assertEq(token.ownerOf(id), address(to)); + assertEq(token.balanceOf(address(to)), 1); + } + + function testFuzzSafeMintToERC721Recipient(uint256 id) public { + ERC721Recipient to = new ERC721Recipient(); + + token.safeMint(address(to), id); + + assertEq(token.ownerOf(id), address(to)); + assertEq(token.balanceOf(address(to)), 1); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), id); + assertBytesEq(to.data(), ""); + } + + function testFuzzSafeMintToERC721RecipientWithData(uint256 id, bytes calldata data) public { + ERC721Recipient to = new ERC721Recipient(); + + token.safeMint(address(to), id, data); + + assertEq(token.ownerOf(id), address(to)); + assertEq(token.balanceOf(address(to)), 1); + + assertEq(to.operator(), address(this)); + assertEq(to.from(), address(0)); + assertEq(to.id(), id); + assertBytesEq(to.data(), data); + } + + function testFailFuzzMintToZero(uint256 id) public { + token.mint(address(0), id); + } + + function testFailFuzzDoubleMint(uint256 id, address to) public { + if (to == address(0)) to = address(0xBEEF); + + token.mint(to, id); + token.mint(to, id); + } + + function testFailFuzzBurnUnMinted(uint256 id) public { + token.burn(id); + } + + function testFailFuzzDoubleBurn(uint256 id, address to) public { + if (to == address(0)) to = address(0xBEEF); + + token.mint(to, id); + + token.burn(id); + token.burn(id); + } + + function testFailFuzzApproveUnMinted(uint256 id, address to) public { + token.approve(to, id); + } + + function testFailFuzzApproveUnAuthorized( + address owner, + uint256 id, + address to + ) public { + if (owner == address(0) || owner == address(this)) owner = address(0xBEEF); + + token.mint(owner, id); + + token.approve(to, id); + } + + function testFailFuzzTransferFromUnOwned( + address from, + address to, + uint256 id + ) public { + token.transferFrom(from, to, id); + } + + function testFailFuzzTransferFromWrongFrom( + address owner, + address from, + address to, + uint256 id + ) public { + if (owner == address(0)) to = address(0xBEEF); + if (from == owner) revert(); + + token.mint(owner, id); + + token.transferFrom(from, to, id); + } + + function testFailFuzzTransferFromToZero(uint256 id) public { + token.mint(address(this), id); + + token.transferFrom(address(this), address(0), id); + } + + function testFailFuzzTransferFromNotOwner( + address from, + address to, + uint256 id + ) public { + if (from == address(this)) from = address(0xBEEF); + + token.mint(from, id); + + token.transferFrom(from, to, id); + } + + function testFailFuzzSafeTransferFromToNonERC721Recipient(uint256 id) public { + token.mint(address(this), id); + + token.safeTransferFrom(address(this), address(new NonERC721Recipient()), id); + } + + function testFailFuzzSafeTransferFromToNonERC721RecipientWithData(uint256 id, bytes calldata data) public { + token.mint(address(this), id); + + token.safeTransferFrom(address(this), address(new NonERC721Recipient()), id, data); + } + + function testFailFuzzSafeTransferFromToRevertingERC721Recipient(uint256 id) public { + token.mint(address(this), id); + + token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), id); + } + + function testFailFuzzSafeTransferFromToRevertingERC721RecipientWithData(uint256 id, bytes calldata data) public { + token.mint(address(this), id); + + token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), id, data); + } + + function testFailFuzzSafeTransferFromToERC721RecipientWithWrongReturnData(uint256 id) public { + token.mint(address(this), id); + + token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), id); + } + + function testFailFuzzSafeTransferFromToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes calldata data) + public + { + token.mint(address(this), id); + + token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), id, data); + } + + function testFailFuzzSafeMintToNonERC721Recipient(uint256 id) public { + token.safeMint(address(new NonERC721Recipient()), id); + } + + function testFailFuzzSafeMintToNonERC721RecipientWithData(uint256 id, bytes calldata data) public { + token.safeMint(address(new NonERC721Recipient()), id, data); + } + + function testFailFuzzSafeMintToRevertingERC721Recipient(uint256 id) public { + token.safeMint(address(new RevertingERC721Recipient()), id); + } + + function testFailFuzzSafeMintToRevertingERC721RecipientWithData(uint256 id, bytes calldata data) public { + token.safeMint(address(new RevertingERC721Recipient()), id, data); + } + + function testFailFuzzSafeMintToERC721RecipientWithWrongReturnData(uint256 id) public { + token.safeMint(address(new WrongReturnDataERC721Recipient()), id); + } + + function testFailFuzzSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes calldata data) public { + token.safeMint(address(new WrongReturnDataERC721Recipient()), id, data); + } + + function testFailFuzzOwnerOfUnminted(uint256 id) public view { + token.ownerOf(id); + } +} diff --git a/lib/solmate/src/test/FixedPointMathLib.t.sol b/lib/solmate/src/test/FixedPointMathLib.t.sol new file mode 100644 index 0000000..d8e7176 --- /dev/null +++ b/lib/solmate/src/test/FixedPointMathLib.t.sol @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; + +contract FixedPointMathLibTest is DSTestPlus { + function testExpWad() public { + assertEq(FixedPointMathLib.expWad(-42139678854452767551), 0); + + assertEq(FixedPointMathLib.expWad(-3e18), 49787068367863942); + assertEq(FixedPointMathLib.expWad(-2e18), 135335283236612691); + assertEq(FixedPointMathLib.expWad(-1e18), 367879441171442321); + + assertEq(FixedPointMathLib.expWad(-0.5e18), 606530659712633423); + assertEq(FixedPointMathLib.expWad(-0.3e18), 740818220681717866); + + assertEq(FixedPointMathLib.expWad(0), 1000000000000000000); + + assertEq(FixedPointMathLib.expWad(0.3e18), 1349858807576003103); + assertEq(FixedPointMathLib.expWad(0.5e18), 1648721270700128146); + + assertEq(FixedPointMathLib.expWad(1e18), 2718281828459045235); + assertEq(FixedPointMathLib.expWad(2e18), 7389056098930650227); + assertEq( + FixedPointMathLib.expWad(3e18), + 20085536923187667741 + // True value: 20085536923187667740.92 + ); + + assertEq( + FixedPointMathLib.expWad(10e18), + 220264657948067165169_80 + // True value: 22026465794806716516957.90 + // Relative error 9.987984547746668e-22 + ); + + assertEq( + FixedPointMathLib.expWad(50e18), + 5184705528587072464_148529318587763226117 + // True value: 5184705528587072464_087453322933485384827.47 + // Relative error: 1.1780031733243328e-20 + ); + + assertEq( + FixedPointMathLib.expWad(100e18), + 268811714181613544841_34666106240937146178367581647816351662017 + // True value: 268811714181613544841_26255515800135873611118773741922415191608 + // Relative error: 3.128803544297531e-22 + ); + + assertEq( + FixedPointMathLib.expWad(135305999368893231588), + 578960446186580976_50144101621524338577433870140581303254786265309376407432913 + // True value: 578960446186580976_49816762928942336782129491980154662247847962410455084893091 + // Relative error: 5.653904247484822e-21 + ); + } + + function testMulWadDown() public { + assertEq(FixedPointMathLib.mulWadDown(2.5e18, 0.5e18), 1.25e18); + assertEq(FixedPointMathLib.mulWadDown(3e18, 1e18), 3e18); + assertEq(FixedPointMathLib.mulWadDown(369, 271), 0); + } + + function testMulWadDownEdgeCases() public { + assertEq(FixedPointMathLib.mulWadDown(0, 1e18), 0); + assertEq(FixedPointMathLib.mulWadDown(1e18, 0), 0); + assertEq(FixedPointMathLib.mulWadDown(0, 0), 0); + } + + function testMulWadUp() public { + assertEq(FixedPointMathLib.mulWadUp(2.5e18, 0.5e18), 1.25e18); + assertEq(FixedPointMathLib.mulWadUp(3e18, 1e18), 3e18); + assertEq(FixedPointMathLib.mulWadUp(369, 271), 1); + } + + function testMulWadUpEdgeCases() public { + assertEq(FixedPointMathLib.mulWadUp(0, 1e18), 0); + assertEq(FixedPointMathLib.mulWadUp(1e18, 0), 0); + assertEq(FixedPointMathLib.mulWadUp(0, 0), 0); + } + + function testDivWadDown() public { + assertEq(FixedPointMathLib.divWadDown(1.25e18, 0.5e18), 2.5e18); + assertEq(FixedPointMathLib.divWadDown(3e18, 1e18), 3e18); + assertEq(FixedPointMathLib.divWadDown(2, 100000000000000e18), 0); + } + + function testDivWadDownEdgeCases() public { + assertEq(FixedPointMathLib.divWadDown(0, 1e18), 0); + } + + function testFailDivWadDownZeroDenominator() public pure { + FixedPointMathLib.divWadDown(1e18, 0); + } + + function testDivWadUp() public { + assertEq(FixedPointMathLib.divWadUp(1.25e18, 0.5e18), 2.5e18); + assertEq(FixedPointMathLib.divWadUp(3e18, 1e18), 3e18); + assertEq(FixedPointMathLib.divWadUp(2, 100000000000000e18), 1); + } + + function testDivWadUpEdgeCases() public { + assertEq(FixedPointMathLib.divWadUp(0, 1e18), 0); + } + + function testFailDivWadUpZeroDenominator() public pure { + FixedPointMathLib.divWadUp(1e18, 0); + } + + function testMulDivDown() public { + assertEq(FixedPointMathLib.mulDivDown(2.5e27, 0.5e27, 1e27), 1.25e27); + assertEq(FixedPointMathLib.mulDivDown(2.5e18, 0.5e18, 1e18), 1.25e18); + assertEq(FixedPointMathLib.mulDivDown(2.5e8, 0.5e8, 1e8), 1.25e8); + assertEq(FixedPointMathLib.mulDivDown(369, 271, 1e2), 999); + + assertEq(FixedPointMathLib.mulDivDown(1e27, 1e27, 2e27), 0.5e27); + assertEq(FixedPointMathLib.mulDivDown(1e18, 1e18, 2e18), 0.5e18); + assertEq(FixedPointMathLib.mulDivDown(1e8, 1e8, 2e8), 0.5e8); + + assertEq(FixedPointMathLib.mulDivDown(2e27, 3e27, 2e27), 3e27); + assertEq(FixedPointMathLib.mulDivDown(3e18, 2e18, 3e18), 2e18); + assertEq(FixedPointMathLib.mulDivDown(2e8, 3e8, 2e8), 3e8); + } + + function testMulDivDownEdgeCases() public { + assertEq(FixedPointMathLib.mulDivDown(0, 1e18, 1e18), 0); + assertEq(FixedPointMathLib.mulDivDown(1e18, 0, 1e18), 0); + assertEq(FixedPointMathLib.mulDivDown(0, 0, 1e18), 0); + } + + function testFailMulDivDownZeroDenominator() public pure { + FixedPointMathLib.mulDivDown(1e18, 1e18, 0); + } + + function testMulDivUp() public { + assertEq(FixedPointMathLib.mulDivUp(2.5e27, 0.5e27, 1e27), 1.25e27); + assertEq(FixedPointMathLib.mulDivUp(2.5e18, 0.5e18, 1e18), 1.25e18); + assertEq(FixedPointMathLib.mulDivUp(2.5e8, 0.5e8, 1e8), 1.25e8); + assertEq(FixedPointMathLib.mulDivUp(369, 271, 1e2), 1000); + + assertEq(FixedPointMathLib.mulDivUp(1e27, 1e27, 2e27), 0.5e27); + assertEq(FixedPointMathLib.mulDivUp(1e18, 1e18, 2e18), 0.5e18); + assertEq(FixedPointMathLib.mulDivUp(1e8, 1e8, 2e8), 0.5e8); + + assertEq(FixedPointMathLib.mulDivUp(2e27, 3e27, 2e27), 3e27); + assertEq(FixedPointMathLib.mulDivUp(3e18, 2e18, 3e18), 2e18); + assertEq(FixedPointMathLib.mulDivUp(2e8, 3e8, 2e8), 3e8); + } + + function testMulDivUpEdgeCases() public { + assertEq(FixedPointMathLib.mulDivUp(0, 1e18, 1e18), 0); + assertEq(FixedPointMathLib.mulDivUp(1e18, 0, 1e18), 0); + assertEq(FixedPointMathLib.mulDivUp(0, 0, 1e18), 0); + } + + function testFailMulDivUpZeroDenominator() public pure { + FixedPointMathLib.mulDivUp(1e18, 1e18, 0); + } + + function testLnWad() public { + assertEq(FixedPointMathLib.lnWad(1e18), 0); + + // Actual: 999999999999999999.8674576… + assertEq(FixedPointMathLib.lnWad(2718281828459045235), 999999999999999999); + + // Actual: 2461607324344817917.963296… + assertEq(FixedPointMathLib.lnWad(11723640096265400935), 2461607324344817918); + } + + function testLnWadSmall() public { + // Actual: -41446531673892822312.3238461… + assertEq(FixedPointMathLib.lnWad(1), -41446531673892822313); + + // Actual: -37708862055609454006.40601608… + assertEq(FixedPointMathLib.lnWad(42), -37708862055609454007); + + // Actual: -32236191301916639576.251880365581… + assertEq(FixedPointMathLib.lnWad(1e4), -32236191301916639577); + + // Actual: -20723265836946411156.161923092… + assertEq(FixedPointMathLib.lnWad(1e9), -20723265836946411157); + } + + function testLnWadBig() public { + // Actual: 135305999368893231589.070344787… + assertEq(FixedPointMathLib.lnWad(2**255 - 1), 135305999368893231589); + + // Actual: 76388489021297880288.605614463571… + assertEq(FixedPointMathLib.lnWad(2**170), 76388489021297880288); + + // Actual: 47276307437780177293.081865… + assertEq(FixedPointMathLib.lnWad(2**128), 47276307437780177293); + } + + function testLnWadNegative() public { + // TODO: Blocked on + // hevm.expectRevert(FixedPointMathLib.LnNegativeUndefined.selector); + // FixedPointMathLib.lnWad(-1); + // FixedPointMathLib.lnWad(-2**255); + } + + function testLnWadOverflow() public { + // TODO: Blocked on + // hevm.expectRevert(FixedPointMathLib.Overflow.selector); + // FixedPointMathLib.lnWad(0); + } + + function testRPow() public { + assertEq(FixedPointMathLib.rpow(2e27, 2, 1e27), 4e27); + assertEq(FixedPointMathLib.rpow(2e18, 2, 1e18), 4e18); + assertEq(FixedPointMathLib.rpow(2e8, 2, 1e8), 4e8); + assertEq(FixedPointMathLib.rpow(8, 3, 1), 512); + } + + function testSqrt() public { + assertEq(FixedPointMathLib.sqrt(0), 0); + assertEq(FixedPointMathLib.sqrt(1), 1); + assertEq(FixedPointMathLib.sqrt(2704), 52); + assertEq(FixedPointMathLib.sqrt(110889), 333); + assertEq(FixedPointMathLib.sqrt(32239684), 5678); + } + + function testLog2() public { + assertEq(FixedPointMathLib.log2(2), 1); + assertEq(FixedPointMathLib.log2(4), 2); + assertEq(FixedPointMathLib.log2(1024), 10); + assertEq(FixedPointMathLib.log2(1048576), 20); + assertEq(FixedPointMathLib.log2(1073741824), 30); + } + + function testFuzzMulWadDown(uint256 x, uint256 y) public { + // Ignore cases where x * y overflows. + unchecked { + if (x != 0 && (x * y) / x != y) return; + } + + assertEq(FixedPointMathLib.mulWadDown(x, y), (x * y) / 1e18); + } + + function testFailFuzzMulWadDownOverflow(uint256 x, uint256 y) public pure { + // Ignore cases where x * y does not overflow. + unchecked { + if ((x * y) / x == y) revert(); + } + + FixedPointMathLib.mulWadDown(x, y); + } + + function testFuzzMulWadUp(uint256 x, uint256 y) public { + // Ignore cases where x * y overflows. + unchecked { + if (x != 0 && (x * y) / x != y) return; + } + + assertEq(FixedPointMathLib.mulWadUp(x, y), x * y == 0 ? 0 : (x * y - 1) / 1e18 + 1); + } + + function testFailFuzzMulWadUpOverflow(uint256 x, uint256 y) public pure { + // Ignore cases where x * y does not overflow. + unchecked { + if ((x * y) / x == y) revert(); + } + + FixedPointMathLib.mulWadUp(x, y); + } + + function testFuzzDivWadDown(uint256 x, uint256 y) public { + // Ignore cases where x * WAD overflows or y is 0. + unchecked { + if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; + } + + assertEq(FixedPointMathLib.divWadDown(x, y), (x * 1e18) / y); + } + + function testFailFuzzDivWadDownOverflow(uint256 x, uint256 y) public pure { + // Ignore cases where x * WAD does not overflow or y is 0. + unchecked { + if (y == 0 || (x * 1e18) / 1e18 == x) revert(); + } + + FixedPointMathLib.divWadDown(x, y); + } + + function testFailFuzzDivWadDownZeroDenominator(uint256 x) public pure { + FixedPointMathLib.divWadDown(x, 0); + } + + function testFuzzDivWadUp(uint256 x, uint256 y) public { + // Ignore cases where x * WAD overflows or y is 0. + unchecked { + if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; + } + + assertEq(FixedPointMathLib.divWadUp(x, y), x == 0 ? 0 : (x * 1e18 - 1) / y + 1); + } + + function testFailFuzzDivWadUpOverflow(uint256 x, uint256 y) public pure { + // Ignore cases where x * WAD does not overflow or y is 0. + unchecked { + if (y == 0 || (x * 1e18) / 1e18 == x) revert(); + } + + FixedPointMathLib.divWadUp(x, y); + } + + function testFailFuzzDivWadUpZeroDenominator(uint256 x) public pure { + FixedPointMathLib.divWadUp(x, 0); + } + + function testFuzzMulDivDown( + uint256 x, + uint256 y, + uint256 denominator + ) public { + // Ignore cases where x * y overflows or denominator is 0. + unchecked { + if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; + } + + assertEq(FixedPointMathLib.mulDivDown(x, y, denominator), (x * y) / denominator); + } + + function testFailFuzzMulDivDownOverflow( + uint256 x, + uint256 y, + uint256 denominator + ) public pure { + // Ignore cases where x * y does not overflow or denominator is 0. + unchecked { + if (denominator == 0 || (x * y) / x == y) revert(); + } + + FixedPointMathLib.mulDivDown(x, y, denominator); + } + + function testFailFuzzMulDivDownZeroDenominator(uint256 x, uint256 y) public pure { + FixedPointMathLib.mulDivDown(x, y, 0); + } + + function testFuzzMulDivUp( + uint256 x, + uint256 y, + uint256 denominator + ) public { + // Ignore cases where x * y overflows or denominator is 0. + unchecked { + if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; + } + + assertEq(FixedPointMathLib.mulDivUp(x, y, denominator), x * y == 0 ? 0 : (x * y - 1) / denominator + 1); + } + + function testFailFuzzMulDivUpOverflow( + uint256 x, + uint256 y, + uint256 denominator + ) public pure { + // Ignore cases where x * y does not overflow or denominator is 0. + unchecked { + if (denominator == 0 || (x * y) / x == y) revert(); + } + + FixedPointMathLib.mulDivUp(x, y, denominator); + } + + function testFailFuzzMulDivUpZeroDenominator(uint256 x, uint256 y) public pure { + FixedPointMathLib.mulDivUp(x, y, 0); + } + + function testFuzzSqrt(uint256 x) public { + uint256 root = FixedPointMathLib.sqrt(x); + uint256 next = root + 1; + + // Ignore cases where next * next overflows. + unchecked { + if (next * next < next) return; + } + + assertTrue(root * root <= x && next * next > x); + } + + function testFuzzLog2() public { + for (uint256 i = 1; i < 255; i++) { + assertEq(FixedPointMathLib.log2((1 << i) - 1), i - 1); + assertEq(FixedPointMathLib.log2((1 << i)), i); + assertEq(FixedPointMathLib.log2((1 << i) + 1), i); + } + } +} diff --git a/lib/solmate/src/test/LibBitmap.t.sol b/lib/solmate/src/test/LibBitmap.t.sol new file mode 100644 index 0000000..7ef6523 --- /dev/null +++ b/lib/solmate/src/test/LibBitmap.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {MockLibBitmap} from "./utils/mocks/MockLibBitmap.sol"; + +contract LibBitmapTest is DSTestPlus { + MockLibBitmap mockLibBitmap; + + function setUp() public { + mockLibBitmap = new MockLibBitmap(); + } + + function testBitmapGet() public { + testBitmapGet(123); + } + + function testBitmapGet(uint256 index) public { + assertFalse(mockLibBitmap.get(index)); + } + + function testBitmapSet() public { + testBitmapSet(123); + } + + function testBitmapSet(uint256 index) public { + mockLibBitmap.set(index); + assertTrue(mockLibBitmap.get(index)); + } + + function testBitmapSetTo() public { + testBitmapSetTo(123, true); + testBitmapSetTo(123, false); + } + + function testBitmapSetTo(uint256 index, bool shouldSet) public { + mockLibBitmap.setTo(index, shouldSet); + assertTrue(mockLibBitmap.get(index) == shouldSet); + } +} diff --git a/lib/solmate/src/test/LibERB.t.sol b/lib/solmate/src/test/LibERB.t.sol new file mode 100644 index 0000000..7c4f52f --- /dev/null +++ b/lib/solmate/src/test/LibERB.t.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {LibERB, LibBoxedERB} from "../utils/LibERB.sol"; + +contract BoxedERBInvariants is DSTestPlus { + using LibBoxedERB for LibBoxedERB.BoxedERB; + + BoxedERB boxedERB; + + function setUp() public { + boxedERB = new BoxedERB(); + } + + function testReproduce() public { + boxedERB.grow(10); + + emit log_uint(boxedERB.availableSlots()); + emit log_uint(boxedERB.populatedSlots()); + + boxedERB.write(1); + + emit log_uint(boxedERB.availableSlots()); + emit log_uint(boxedERB.populatedSlots()); + + require(boxedERB.read().updateNumber - 1 == boxedERB.readOffset(1).updateNumber); + } + + function invariantMonotonicUpdateNumber() public { + if (boxedERB.populatedSlots() < 2) return; + + emit log_uint(boxedERB.read().updateNumber - 1); + emit log_uint(boxedERB.readOffset(1).updateNumber); + + require(boxedERB.read().updateNumber - 1 == boxedERB.readOffset(1).updateNumber); + } + + function invariantPopulatedSlotsLeToAvailable() public { + emit log_uint(boxedERB.populatedSlots()); + emit log_uint(boxedERB.availableSlots()); + require(boxedERB.populatedSlots() <= boxedERB.availableSlots()); + // assertLe(boxedERB.populatedSlots(), boxedERB.availableSlots()); + } +} + +contract BoxedERB { + using LibBoxedERB for LibBoxedERB.BoxedERB; + + LibBoxedERB.BoxedERB public boxedERB; + + constructor() { + boxedERB.init(); + } + + function write(uint224 x) public { + boxedERB.write(x); + } + + function grow(uint16 growBy) public { + boxedERB.grow(growBy); + } + + function read() public view returns (LibERB.ERBValue memory) { + return boxedERB.read(); + } + + function readOffset(uint32 offset) public view returns (LibERB.ERBValue memory) { + return boxedERB.readOffset(offset); + } + + function populatedSlots() public view returns (uint256) { + return boxedERB.populatedSlots; + } + + function availableSlots() public view returns (uint256) { + return boxedERB.availableSlots; + } +} diff --git a/lib/solmate/src/test/MerkleProof.t.sol b/lib/solmate/src/test/MerkleProof.t.sol new file mode 100644 index 0000000..37726f3 --- /dev/null +++ b/lib/solmate/src/test/MerkleProof.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {MerkleProofLib} from "../utils/MerkleProofLib.sol"; + +contract MerkleProofTest is DSTestPlus { + function testVerifyEmptyMerkleProofSuppliedLeafAndRootSame() public { + bytes32[] memory proof; + assertBoolEq(this.verify(proof, 0x00, 0x00), true); + } + + function testVerifyEmptyMerkleProofSuppliedLeafAndRootDifferent() public { + bytes32[] memory proof; + bytes32 leaf = "a"; + assertBoolEq(this.verify(proof, 0x00, leaf), false); + } + + function testValidProofSupplied() public { + // Merkle tree created from leaves ['a', 'b', 'c']. + // Leaf is 'a'. + bytes32[] memory proof = new bytes32[](2); + proof[0] = 0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510; + proof[1] = 0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2; + bytes32 root = 0x5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7; + bytes32 leaf = 0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb; + assertBoolEq(this.verify(proof, root, leaf), true); + } + + function testVerifyInvalidProofSupplied() public { + // Merkle tree created from leaves ['a', 'b', 'c']. + // Leaf is 'a'. + // Proof is same as testValidProofSupplied but last byte of first element is modified. + bytes32[] memory proof = new bytes32[](2); + proof[0] = 0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5511; + proof[1] = 0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2; + bytes32 root = 0x5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7; + bytes32 leaf = 0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb; + assertBoolEq(this.verify(proof, root, leaf), false); + } + + function verify( + bytes32[] calldata proof, + bytes32 root, + bytes32 leaf + ) external pure returns (bool) { + return MerkleProofLib.verify(proof, root, leaf); + } +} diff --git a/lib/solmate/src/test/MultiRolesAuthority.t.sol b/lib/solmate/src/test/MultiRolesAuthority.t.sol new file mode 100644 index 0000000..6503192 --- /dev/null +++ b/lib/solmate/src/test/MultiRolesAuthority.t.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {MockAuthority} from "./utils/mocks/MockAuthority.sol"; + +import {Authority} from "../auth/Auth.sol"; + +import {MultiRolesAuthority} from "../auth/authorities/MultiRolesAuthority.sol"; + +contract MultiRolesAuthorityTest is DSTestPlus { + MultiRolesAuthority multiRolesAuthority; + + function setUp() public { + multiRolesAuthority = new MultiRolesAuthority(address(this), Authority(address(0))); + } + + function testSetRoles() public { + assertFalse(multiRolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); + assertTrue(multiRolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); + assertFalse(multiRolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); + } + + function testSetRoleCapabilities() public { + assertFalse(multiRolesAuthority.doesRoleHaveCapability(0, 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.doesRoleHaveCapability(0, 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, false); + assertFalse(multiRolesAuthority.doesRoleHaveCapability(0, 0xBEEFCAFE)); + } + + function testSetPublicCapabilities() public { + assertFalse(multiRolesAuthority.isCapabilityPublic(0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.isCapabilityPublic(0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, false); + assertFalse(multiRolesAuthority.isCapabilityPublic(0xBEEFCAFE)); + } + + function testSetTargetCustomAuthority() public { + assertEq(address(multiRolesAuthority.getTargetCustomAuthority(address(0xBEEF))), address(0)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xBEEF), Authority(address(0xCAFE))); + assertEq(address(multiRolesAuthority.getTargetCustomAuthority(address(0xBEEF))), address(0xCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xBEEF), Authority(address(0))); + assertEq(address(multiRolesAuthority.getTargetCustomAuthority(address(0xBEEF))), address(0)); + } + + function testCanCallWithAuthorizedRole() public { + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, false); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testCanCallPublicCapability() public { + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, false); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testCanCallWithCustomAuthority() public { + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), Authority(address(0))); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testCanCallWithCustomAuthorityOverridesPublicCapability() public { + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, false); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), Authority(address(0))); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testCanCallWithCustomAuthorityOverridesUserWithRole() public { + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), Authority(address(0))); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); + assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, false); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); + assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testFuzzSetRoles(address user, uint8 role) public { + assertFalse(multiRolesAuthority.doesUserHaveRole(user, role)); + + multiRolesAuthority.setUserRole(user, role, true); + assertTrue(multiRolesAuthority.doesUserHaveRole(user, role)); + + multiRolesAuthority.setUserRole(user, role, false); + assertFalse(multiRolesAuthority.doesUserHaveRole(user, role)); + } + + function testFuzzSetRoleCapabilities(uint8 role, bytes4 functionSig) public { + assertFalse(multiRolesAuthority.doesRoleHaveCapability(role, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, true); + assertTrue(multiRolesAuthority.doesRoleHaveCapability(role, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, false); + assertFalse(multiRolesAuthority.doesRoleHaveCapability(role, functionSig)); + } + + function testFuzzSetPublicCapabilities(bytes4 functionSig) public { + assertFalse(multiRolesAuthority.isCapabilityPublic(functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, true); + assertTrue(multiRolesAuthority.isCapabilityPublic(functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, false); + assertFalse(multiRolesAuthority.isCapabilityPublic(functionSig)); + } + + function testFuzzSetTargetCustomAuthority(address user, Authority customAuthority) public { + assertEq(address(multiRolesAuthority.getTargetCustomAuthority(user)), address(0)); + + multiRolesAuthority.setTargetCustomAuthority(user, customAuthority); + assertEq(address(multiRolesAuthority.getTargetCustomAuthority(user)), address(customAuthority)); + + multiRolesAuthority.setTargetCustomAuthority(user, Authority(address(0))); + assertEq(address(multiRolesAuthority.getTargetCustomAuthority(user)), address(0)); + } + + function testFuzzCanCallWithAuthorizedRole( + address user, + uint8 role, + address target, + bytes4 functionSig + ) public { + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setUserRole(user, role, true); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, false); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setUserRole(user, role, false); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + } + + function testFuzzCanCallPublicCapability( + address user, + address target, + bytes4 functionSig + ) public { + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, false); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + } + + function testFuzzCanCallWithCustomAuthority( + address user, + address target, + bytes4 functionSig + ) public { + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, Authority(address(0))); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + } + + function testFuzzCanCallWithCustomAuthorityOverridesPublicCapability( + address user, + address target, + bytes4 functionSig + ) public { + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, false); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, Authority(address(0))); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setPublicCapability(functionSig, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + } + + function testFuzzCanCallWithCustomAuthorityOverridesUserWithRole( + address user, + uint8 role, + address target, + bytes4 functionSig + ) public { + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setUserRole(user, role, true); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setUserRole(user, role, false); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setTargetCustomAuthority(target, Authority(address(0))); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setUserRole(user, role, true); + assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setRoleCapability(role, functionSig, false); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + + multiRolesAuthority.setUserRole(user, role, false); + assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); + } +} diff --git a/lib/solmate/src/test/Owned.t.sol b/lib/solmate/src/test/Owned.t.sol new file mode 100644 index 0000000..6c30c10 --- /dev/null +++ b/lib/solmate/src/test/Owned.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {MockOwned} from "./utils/mocks/MockOwned.sol"; + +contract OwnedTest is DSTestPlus { + MockOwned mockOwned; + + function setUp() public { + mockOwned = new MockOwned(); + } + + function testSetOwner() public { + testSetOwner(address(0xBEEF)); + } + + function testCallFunctionAsNonOwner() public { + testCallFunctionAsNonOwner(address(0)); + } + + function testCallFunctionAsOwner() public { + mockOwned.updateFlag(); + } + + function testSetOwner(address newOwner) public { + mockOwned.setOwner(newOwner); + + assertEq(mockOwned.owner(), newOwner); + } + + function testCallFunctionAsNonOwner(address owner) public { + hevm.assume(owner != address(this)); + + mockOwned.setOwner(owner); + + hevm.expectRevert("UNAUTHORIZED"); + mockOwned.updateFlag(); + } +} diff --git a/lib/solmate/src/test/ReentrancyGuard.t.sol b/lib/solmate/src/test/ReentrancyGuard.t.sol new file mode 100644 index 0000000..048426f --- /dev/null +++ b/lib/solmate/src/test/ReentrancyGuard.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {ReentrancyGuard} from "../utils/ReentrancyGuard.sol"; + +contract RiskyContract is ReentrancyGuard { + uint256 public enterTimes; + + function unprotectedCall() public { + enterTimes++; + + if (enterTimes > 1) return; + + this.protectedCall(); + } + + function protectedCall() public nonReentrant { + enterTimes++; + + if (enterTimes > 1) return; + + this.protectedCall(); + } + + function overprotectedCall() public nonReentrant {} +} + +contract ReentrancyGuardTest is DSTestPlus { + RiskyContract riskyContract; + + function setUp() public { + riskyContract = new RiskyContract(); + } + + function invariantReentrancyStatusAlways1() public { + assertEq(uint256(hevm.load(address(riskyContract), 0)), 1); + } + + function testFailUnprotectedCall() public { + riskyContract.unprotectedCall(); + + assertEq(riskyContract.enterTimes(), 1); + } + + function testProtectedCall() public { + try riskyContract.protectedCall() { + fail("Reentrancy Guard Failed To Stop Attacker"); + } catch {} + } + + function testNoReentrancy() public { + riskyContract.overprotectedCall(); + } +} diff --git a/lib/solmate/src/test/RolesAuthority.t.sol b/lib/solmate/src/test/RolesAuthority.t.sol new file mode 100644 index 0000000..6c36cb3 --- /dev/null +++ b/lib/solmate/src/test/RolesAuthority.t.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {MockAuthority} from "./utils/mocks/MockAuthority.sol"; + +import {Authority} from "../auth/Auth.sol"; + +import {RolesAuthority} from "../auth/authorities/RolesAuthority.sol"; + +contract RolesAuthorityTest is DSTestPlus { + RolesAuthority rolesAuthority; + + function setUp() public { + rolesAuthority = new RolesAuthority(address(this), Authority(address(0))); + } + + function testSetRoles() public { + assertFalse(rolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); + + rolesAuthority.setUserRole(address(0xBEEF), 0, true); + assertTrue(rolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); + + rolesAuthority.setUserRole(address(0xBEEF), 0, false); + assertFalse(rolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); + } + + function testSetRoleCapabilities() public { + assertFalse(rolesAuthority.doesRoleHaveCapability(0, address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, true); + assertTrue(rolesAuthority.doesRoleHaveCapability(0, address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, false); + assertFalse(rolesAuthority.doesRoleHaveCapability(0, address(0xCAFE), 0xBEEFCAFE)); + } + + function testSetPublicCapabilities() public { + assertFalse(rolesAuthority.isCapabilityPublic(address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, true); + assertTrue(rolesAuthority.isCapabilityPublic(address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, false); + assertFalse(rolesAuthority.isCapabilityPublic(address(0xCAFE), 0xBEEFCAFE)); + } + + function testCanCallWithAuthorizedRole() public { + assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setUserRole(address(0xBEEF), 0, true); + assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, true); + assertTrue(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, false); + assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, true); + assertTrue(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setUserRole(address(0xBEEF), 0, false); + assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testCanCallPublicCapability() public { + assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, true); + assertTrue(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + + rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, false); + assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); + } + + function testFuzzSetRoles(address user, uint8 role) public { + assertFalse(rolesAuthority.doesUserHaveRole(user, role)); + + rolesAuthority.setUserRole(user, role, true); + assertTrue(rolesAuthority.doesUserHaveRole(user, role)); + + rolesAuthority.setUserRole(user, role, false); + assertFalse(rolesAuthority.doesUserHaveRole(user, role)); + } + + function testFuzzSetRoleCapabilities( + uint8 role, + address target, + bytes4 functionSig + ) public { + assertFalse(rolesAuthority.doesRoleHaveCapability(role, target, functionSig)); + + rolesAuthority.setRoleCapability(role, target, functionSig, true); + assertTrue(rolesAuthority.doesRoleHaveCapability(role, target, functionSig)); + + rolesAuthority.setRoleCapability(role, target, functionSig, false); + assertFalse(rolesAuthority.doesRoleHaveCapability(role, target, functionSig)); + } + + function testFuzzSetPublicCapabilities(address target, bytes4 functionSig) public { + assertFalse(rolesAuthority.isCapabilityPublic(target, functionSig)); + + rolesAuthority.setPublicCapability(target, functionSig, true); + assertTrue(rolesAuthority.isCapabilityPublic(target, functionSig)); + + rolesAuthority.setPublicCapability(target, functionSig, false); + assertFalse(rolesAuthority.isCapabilityPublic(target, functionSig)); + } + + function testFuzzCanCallWithAuthorizedRole( + address user, + uint8 role, + address target, + bytes4 functionSig + ) public { + assertFalse(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setUserRole(user, role, true); + assertFalse(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setRoleCapability(role, target, functionSig, true); + assertTrue(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setRoleCapability(role, target, functionSig, false); + assertFalse(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setRoleCapability(role, target, functionSig, true); + assertTrue(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setUserRole(user, role, false); + assertFalse(rolesAuthority.canCall(user, target, functionSig)); + } + + function testFuzzCanCallPublicCapability( + address user, + address target, + bytes4 functionSig + ) public { + assertFalse(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setPublicCapability(target, functionSig, true); + assertTrue(rolesAuthority.canCall(user, target, functionSig)); + + rolesAuthority.setPublicCapability(target, functionSig, false); + assertFalse(rolesAuthority.canCall(user, target, functionSig)); + } +} diff --git a/lib/solmate/src/test/SSTORE2.t.sol b/lib/solmate/src/test/SSTORE2.t.sol new file mode 100644 index 0000000..c9f9fd0 --- /dev/null +++ b/lib/solmate/src/test/SSTORE2.t.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {SSTORE2} from "../utils/SSTORE2.sol"; + +contract SSTORE2Test is DSTestPlus { + function testWriteRead() public { + bytes memory testBytes = abi.encode("this is a test"); + + address pointer = SSTORE2.write(testBytes); + + assertBytesEq(SSTORE2.read(pointer), testBytes); + } + + function testWriteReadFullStartBound() public { + assertBytesEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 0), hex"11223344"); + } + + function testWriteReadCustomStartBound() public { + assertBytesEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 1), hex"223344"); + } + + function testWriteReadFullBoundedRead() public { + bytes memory testBytes = abi.encode("this is a test"); + + assertBytesEq(SSTORE2.read(SSTORE2.write(testBytes), 0, testBytes.length), testBytes); + } + + function testWriteReadCustomBounds() public { + assertBytesEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 1, 3), hex"2233"); + } + + function testWriteReadEmptyBound() public { + SSTORE2.read(SSTORE2.write(hex"11223344"), 3, 3); + } + + function testFailReadInvalidPointer() public view { + SSTORE2.read(DEAD_ADDRESS); + } + + function testFailReadInvalidPointerCustomStartBound() public view { + SSTORE2.read(DEAD_ADDRESS, 1); + } + + function testFailReadInvalidPointerCustomBounds() public view { + SSTORE2.read(DEAD_ADDRESS, 2, 4); + } + + function testFailWriteReadOutOfStartBound() public { + SSTORE2.read(SSTORE2.write(hex"11223344"), 41000); + } + + function testFailWriteReadEmptyOutOfBounds() public { + SSTORE2.read(SSTORE2.write(hex"11223344"), 42000, 42000); + } + + function testFailWriteReadOutOfBounds() public { + SSTORE2.read(SSTORE2.write(hex"11223344"), 41000, 42000); + } + + function testFuzzWriteRead(bytes calldata testBytes, bytes calldata brutalizeWith) + public + brutalizeMemory(brutalizeWith) + { + assertBytesEq(SSTORE2.read(SSTORE2.write(testBytes)), testBytes); + } + + function testFuzzWriteReadCustomStartBound( + bytes calldata testBytes, + uint256 startIndex, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if (testBytes.length == 0) return; + + startIndex = bound(startIndex, 0, testBytes.length); + + assertBytesEq(SSTORE2.read(SSTORE2.write(testBytes), startIndex), bytes(testBytes[startIndex:])); + } + + function testFuzzWriteReadCustomBounds( + bytes calldata testBytes, + uint256 startIndex, + uint256 endIndex, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if (testBytes.length == 0) return; + + endIndex = bound(endIndex, 0, testBytes.length); + startIndex = bound(startIndex, 0, testBytes.length); + + if (startIndex > endIndex) return; + + assertBytesEq( + SSTORE2.read(SSTORE2.write(testBytes), startIndex, endIndex), + bytes(testBytes[startIndex:endIndex]) + ); + } + + function testFailFuzzReadInvalidPointer(address pointer, bytes calldata brutalizeWith) + public + view + brutalizeMemory(brutalizeWith) + { + if (pointer.code.length > 0) revert(); + + SSTORE2.read(pointer); + } + + function testFailFuzzReadInvalidPointerCustomStartBound( + address pointer, + uint256 startIndex, + bytes calldata brutalizeWith + ) public view brutalizeMemory(brutalizeWith) { + if (pointer.code.length > 0) revert(); + + SSTORE2.read(pointer, startIndex); + } + + function testFailFuzzReadInvalidPointerCustomBounds( + address pointer, + uint256 startIndex, + uint256 endIndex, + bytes calldata brutalizeWith + ) public view brutalizeMemory(brutalizeWith) { + if (pointer.code.length > 0) revert(); + + SSTORE2.read(pointer, startIndex, endIndex); + } + + function testFailFuzzWriteReadCustomStartBoundOutOfRange( + bytes calldata testBytes, + uint256 startIndex, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + startIndex = bound(startIndex, testBytes.length + 1, type(uint256).max); + + SSTORE2.read(SSTORE2.write(testBytes), startIndex); + } + + function testFailFuzzWriteReadCustomBoundsOutOfRange( + bytes calldata testBytes, + uint256 startIndex, + uint256 endIndex, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + endIndex = bound(endIndex, testBytes.length + 1, type(uint256).max); + + SSTORE2.read(SSTORE2.write(testBytes), startIndex, endIndex); + } +} diff --git a/lib/solmate/src/test/SafeCastLib.t.sol b/lib/solmate/src/test/SafeCastLib.t.sol new file mode 100644 index 0000000..b97b244 --- /dev/null +++ b/lib/solmate/src/test/SafeCastLib.t.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {SafeCastLib} from "../utils/SafeCastLib.sol"; + +contract SafeCastLibTest is DSTestPlus { + function testSafeCastTo248() public { + assertEq(SafeCastLib.safeCastTo248(2.5e45), 2.5e45); + assertEq(SafeCastLib.safeCastTo248(2.5e27), 2.5e27); + } + + function testSafeCastTo224() public { + assertEq(SafeCastLib.safeCastTo224(2.5e36), 2.5e36); + assertEq(SafeCastLib.safeCastTo224(2.5e27), 2.5e27); + } + + function testSafeCastTo192() public { + assertEq(SafeCastLib.safeCastTo192(2.5e36), 2.5e36); + assertEq(SafeCastLib.safeCastTo192(2.5e27), 2.5e27); + } + + function testSafeCastTo160() public { + assertEq(SafeCastLib.safeCastTo160(2.5e36), 2.5e36); + assertEq(SafeCastLib.safeCastTo160(2.5e27), 2.5e27); + } + + function testSafeCastTo128() public { + assertEq(SafeCastLib.safeCastTo128(2.5e27), 2.5e27); + assertEq(SafeCastLib.safeCastTo128(2.5e18), 2.5e18); + } + + function testSafeCastTo96() public { + assertEq(SafeCastLib.safeCastTo96(2.5e18), 2.5e18); + assertEq(SafeCastLib.safeCastTo96(2.5e17), 2.5e17); + } + + function testSafeCastTo64() public { + assertEq(SafeCastLib.safeCastTo64(2.5e18), 2.5e18); + assertEq(SafeCastLib.safeCastTo64(2.5e17), 2.5e17); + } + + function testSafeCastTo32() public { + assertEq(SafeCastLib.safeCastTo32(2.5e8), 2.5e8); + assertEq(SafeCastLib.safeCastTo32(2.5e7), 2.5e7); + } + + function testSafeCastTo8() public { + assertEq(SafeCastLib.safeCastTo8(100), 100); + assertEq(SafeCastLib.safeCastTo8(250), 250); + } + + function testFailSafeCastTo248() public pure { + SafeCastLib.safeCastTo248(type(uint248).max + 1); + } + + function testFailSafeCastTo224() public pure { + SafeCastLib.safeCastTo224(type(uint224).max + 1); + } + + function testFailSafeCastTo192() public pure { + SafeCastLib.safeCastTo192(type(uint192).max + 1); + } + + function testFailSafeCastTo160() public pure { + SafeCastLib.safeCastTo160(type(uint160).max + 1); + } + + function testFailSafeCastTo128() public pure { + SafeCastLib.safeCastTo128(type(uint128).max + 1); + } + + function testFailSafeCastTo96() public pure { + SafeCastLib.safeCastTo96(type(uint96).max + 1); + } + + function testFailSafeCastTo64() public pure { + SafeCastLib.safeCastTo64(type(uint64).max + 1); + } + + function testFailSafeCastTo32() public pure { + SafeCastLib.safeCastTo32(type(uint32).max + 1); + } + + function testFailSafeCastTo8() public pure { + SafeCastLib.safeCastTo8(type(uint8).max + 1); + } + + function testFuzzSafeCastTo248(uint256 x) public { + x = bound(x, 0, type(uint248).max); + + assertEq(SafeCastLib.safeCastTo248(x), x); + } + + function testFuzzSafeCastTo224(uint256 x) public { + x = bound(x, 0, type(uint224).max); + + assertEq(SafeCastLib.safeCastTo224(x), x); + } + + function testFuzzSafeCastTo192(uint256 x) public { + x = bound(x, 0, type(uint192).max); + + assertEq(SafeCastLib.safeCastTo192(x), x); + } + + function testFuzzSafeCastTo160(uint256 x) public { + x = bound(x, 0, type(uint160).max); + + assertEq(SafeCastLib.safeCastTo160(x), x); + } + + function testFuzzSafeCastTo128(uint256 x) public { + x = bound(x, 0, type(uint128).max); + + assertEq(SafeCastLib.safeCastTo128(x), x); + } + + function testFuzzSafeCastTo96(uint256 x) public { + x = bound(x, 0, type(uint96).max); + + assertEq(SafeCastLib.safeCastTo96(x), x); + } + + function testFuzzSafeCastTo64(uint256 x) public { + x = bound(x, 0, type(uint64).max); + + assertEq(SafeCastLib.safeCastTo64(x), x); + } + + function testFuzzSafeCastTo32(uint256 x) public { + x = bound(x, 0, type(uint32).max); + + assertEq(SafeCastLib.safeCastTo32(x), x); + } + + function testFuzzSafeCastTo8(uint256 x) public { + x = bound(x, 0, type(uint8).max); + + assertEq(SafeCastLib.safeCastTo8(x), x); + } + + function testFailFuzzSafeCastTo248(uint256 x) public { + x = bound(x, type(uint248).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo248(x); + } + + function testFailFuzzSafeCastTo224(uint256 x) public { + x = bound(x, type(uint224).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo224(x); + } + + function testFailFuzzSafeCastTo192(uint256 x) public { + x = bound(x, type(uint192).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo192(x); + } + + function testFailFuzzSafeCastTo160(uint256 x) public { + x = bound(x, type(uint160).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo160(x); + } + + function testFailFuzzSafeCastTo128(uint256 x) public { + x = bound(x, type(uint128).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo128(x); + } + + function testFailFuzzSafeCastTo96(uint256 x) public { + x = bound(x, type(uint96).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo96(x); + } + + function testFailFuzzSafeCastTo64(uint256 x) public { + x = bound(x, type(uint64).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo64(x); + } + + function testFailFuzzSafeCastTo32(uint256 x) public { + x = bound(x, type(uint32).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo32(x); + } + + function testFailFuzzSafeCastTo8(uint256 x) public { + x = bound(x, type(uint8).max + 1, type(uint256).max); + + SafeCastLib.safeCastTo8(x); + } +} diff --git a/lib/solmate/src/test/SafeTransferLib.t.sol b/lib/solmate/src/test/SafeTransferLib.t.sol new file mode 100644 index 0000000..8bed492 --- /dev/null +++ b/lib/solmate/src/test/SafeTransferLib.t.sol @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {MockERC20} from "./utils/mocks/MockERC20.sol"; +import {RevertingToken} from "./utils/weird-tokens/RevertingToken.sol"; +import {ReturnsTwoToken} from "./utils/weird-tokens/ReturnsTwoToken.sol"; +import {ReturnsFalseToken} from "./utils/weird-tokens/ReturnsFalseToken.sol"; +import {MissingReturnToken} from "./utils/weird-tokens/MissingReturnToken.sol"; +import {ReturnsTooMuchToken} from "./utils/weird-tokens/ReturnsTooMuchToken.sol"; +import {ReturnsGarbageToken} from "./utils/weird-tokens/ReturnsGarbageToken.sol"; +import {ReturnsTooLittleToken} from "./utils/weird-tokens/ReturnsTooLittleToken.sol"; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; + +import {ERC20} from "../tokens/ERC20.sol"; +import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; + +contract SafeTransferLibTest is DSTestPlus { + RevertingToken reverting; + ReturnsTwoToken returnsTwo; + ReturnsFalseToken returnsFalse; + MissingReturnToken missingReturn; + ReturnsTooMuchToken returnsTooMuch; + ReturnsGarbageToken returnsGarbage; + ReturnsTooLittleToken returnsTooLittle; + + MockERC20 erc20; + + function setUp() public { + reverting = new RevertingToken(); + returnsTwo = new ReturnsTwoToken(); + returnsFalse = new ReturnsFalseToken(); + missingReturn = new MissingReturnToken(); + returnsTooMuch = new ReturnsTooMuchToken(); + returnsGarbage = new ReturnsGarbageToken(); + returnsTooLittle = new ReturnsTooLittleToken(); + + erc20 = new MockERC20("StandardToken", "ST", 18); + erc20.mint(address(this), type(uint256).max); + } + + function testTransferWithMissingReturn() public { + verifySafeTransfer(address(missingReturn), address(0xBEEF), 1e18); + } + + function testTransferWithStandardERC20() public { + verifySafeTransfer(address(erc20), address(0xBEEF), 1e18); + } + + function testTransferWithReturnsTooMuch() public { + verifySafeTransfer(address(returnsTooMuch), address(0xBEEF), 1e18); + } + + function testTransferWithNonContract() public { + SafeTransferLib.safeTransfer(ERC20(address(0xBADBEEF)), address(0xBEEF), 1e18); + } + + function testTransferFromWithMissingReturn() public { + verifySafeTransferFrom(address(missingReturn), address(0xFEED), address(0xBEEF), 1e18); + } + + function testTransferFromWithStandardERC20() public { + verifySafeTransferFrom(address(erc20), address(0xFEED), address(0xBEEF), 1e18); + } + + function testTransferFromWithReturnsTooMuch() public { + verifySafeTransferFrom(address(returnsTooMuch), address(0xFEED), address(0xBEEF), 1e18); + } + + function testTransferFromWithNonContract() public { + SafeTransferLib.safeTransferFrom(ERC20(address(0xBADBEEF)), address(0xFEED), address(0xBEEF), 1e18); + } + + function testApproveWithMissingReturn() public { + verifySafeApprove(address(missingReturn), address(0xBEEF), 1e18); + } + + function testApproveWithStandardERC20() public { + verifySafeApprove(address(erc20), address(0xBEEF), 1e18); + } + + function testApproveWithReturnsTooMuch() public { + verifySafeApprove(address(returnsTooMuch), address(0xBEEF), 1e18); + } + + function testApproveWithNonContract() public { + SafeTransferLib.safeApprove(ERC20(address(0xBADBEEF)), address(0xBEEF), 1e18); + } + + function testTransferETH() public { + SafeTransferLib.safeTransferETH(address(0xBEEF), 1e18); + } + + function testFailTransferWithReturnsFalse() public { + verifySafeTransfer(address(returnsFalse), address(0xBEEF), 1e18); + } + + function testFailTransferWithReverting() public { + verifySafeTransfer(address(reverting), address(0xBEEF), 1e18); + } + + function testFailTransferWithReturnsTooLittle() public { + verifySafeTransfer(address(returnsTooLittle), address(0xBEEF), 1e18); + } + + function testFailTransferFromWithReturnsFalse() public { + verifySafeTransferFrom(address(returnsFalse), address(0xFEED), address(0xBEEF), 1e18); + } + + function testFailTransferFromWithReverting() public { + verifySafeTransferFrom(address(reverting), address(0xFEED), address(0xBEEF), 1e18); + } + + function testFailTransferFromWithReturnsTooLittle() public { + verifySafeTransferFrom(address(returnsTooLittle), address(0xFEED), address(0xBEEF), 1e18); + } + + function testFailApproveWithReturnsFalse() public { + verifySafeApprove(address(returnsFalse), address(0xBEEF), 1e18); + } + + function testFailApproveWithReverting() public { + verifySafeApprove(address(reverting), address(0xBEEF), 1e18); + } + + function testFailApproveWithReturnsTooLittle() public { + verifySafeApprove(address(returnsTooLittle), address(0xBEEF), 1e18); + } + + function testFuzzTransferWithMissingReturn( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(missingReturn), to, amount); + } + + function testFuzzTransferWithStandardERC20( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(erc20), to, amount); + } + + function testFuzzTransferWithReturnsTooMuch( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(returnsTooMuch), to, amount); + } + + function testFuzzTransferWithGarbage( + address to, + uint256 amount, + bytes memory garbage, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if ( + (garbage.length < 32 || + (garbage[0] != 0 || + garbage[1] != 0 || + garbage[2] != 0 || + garbage[3] != 0 || + garbage[4] != 0 || + garbage[5] != 0 || + garbage[6] != 0 || + garbage[7] != 0 || + garbage[8] != 0 || + garbage[9] != 0 || + garbage[10] != 0 || + garbage[11] != 0 || + garbage[12] != 0 || + garbage[13] != 0 || + garbage[14] != 0 || + garbage[15] != 0 || + garbage[16] != 0 || + garbage[17] != 0 || + garbage[18] != 0 || + garbage[19] != 0 || + garbage[20] != 0 || + garbage[21] != 0 || + garbage[22] != 0 || + garbage[23] != 0 || + garbage[24] != 0 || + garbage[25] != 0 || + garbage[26] != 0 || + garbage[27] != 0 || + garbage[28] != 0 || + garbage[29] != 0 || + garbage[30] != 0 || + garbage[31] != bytes1(0x01))) && garbage.length != 0 + ) return; + + returnsGarbage.setGarbage(garbage); + + verifySafeTransfer(address(returnsGarbage), to, amount); + } + + function testFuzzTransferWithNonContract( + address nonContract, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) return; + + SafeTransferLib.safeTransfer(ERC20(nonContract), to, amount); + } + + function testFailTransferETHToContractWithoutFallback() public { + SafeTransferLib.safeTransferETH(address(this), 1e18); + } + + function testFuzzTransferFromWithMissingReturn( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(missingReturn), from, to, amount); + } + + function testFuzzTransferFromWithStandardERC20( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(erc20), from, to, amount); + } + + function testFuzzTransferFromWithReturnsTooMuch( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(returnsTooMuch), from, to, amount); + } + + function testFuzzTransferFromWithGarbage( + address from, + address to, + uint256 amount, + bytes memory garbage, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if ( + (garbage.length < 32 || + (garbage[0] != 0 || + garbage[1] != 0 || + garbage[2] != 0 || + garbage[3] != 0 || + garbage[4] != 0 || + garbage[5] != 0 || + garbage[6] != 0 || + garbage[7] != 0 || + garbage[8] != 0 || + garbage[9] != 0 || + garbage[10] != 0 || + garbage[11] != 0 || + garbage[12] != 0 || + garbage[13] != 0 || + garbage[14] != 0 || + garbage[15] != 0 || + garbage[16] != 0 || + garbage[17] != 0 || + garbage[18] != 0 || + garbage[19] != 0 || + garbage[20] != 0 || + garbage[21] != 0 || + garbage[22] != 0 || + garbage[23] != 0 || + garbage[24] != 0 || + garbage[25] != 0 || + garbage[26] != 0 || + garbage[27] != 0 || + garbage[28] != 0 || + garbage[29] != 0 || + garbage[30] != 0 || + garbage[31] != bytes1(0x01))) && garbage.length != 0 + ) return; + + returnsGarbage.setGarbage(garbage); + + verifySafeTransferFrom(address(returnsGarbage), from, to, amount); + } + + function testFuzzTransferFromWithNonContract( + address nonContract, + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) return; + + SafeTransferLib.safeTransferFrom(ERC20(nonContract), from, to, amount); + } + + function testFuzzApproveWithMissingReturn( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(missingReturn), to, amount); + } + + function testFuzzApproveWithStandardERC20( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(erc20), to, amount); + } + + function testFuzzApproveWithReturnsTooMuch( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(returnsTooMuch), to, amount); + } + + function testFuzzApproveWithGarbage( + address to, + uint256 amount, + bytes memory garbage, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if ( + (garbage.length < 32 || + (garbage[0] != 0 || + garbage[1] != 0 || + garbage[2] != 0 || + garbage[3] != 0 || + garbage[4] != 0 || + garbage[5] != 0 || + garbage[6] != 0 || + garbage[7] != 0 || + garbage[8] != 0 || + garbage[9] != 0 || + garbage[10] != 0 || + garbage[11] != 0 || + garbage[12] != 0 || + garbage[13] != 0 || + garbage[14] != 0 || + garbage[15] != 0 || + garbage[16] != 0 || + garbage[17] != 0 || + garbage[18] != 0 || + garbage[19] != 0 || + garbage[20] != 0 || + garbage[21] != 0 || + garbage[22] != 0 || + garbage[23] != 0 || + garbage[24] != 0 || + garbage[25] != 0 || + garbage[26] != 0 || + garbage[27] != 0 || + garbage[28] != 0 || + garbage[29] != 0 || + garbage[30] != 0 || + garbage[31] != bytes1(0x01))) && garbage.length != 0 + ) return; + + returnsGarbage.setGarbage(garbage); + + verifySafeApprove(address(returnsGarbage), to, amount); + } + + function testFuzzApproveWithNonContract( + address nonContract, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) return; + + SafeTransferLib.safeApprove(ERC20(nonContract), to, amount); + } + + function testFuzzTransferETH( + address recipient, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + // Transferring to msg.sender can fail because it's possible to overflow their ETH balance as it begins non-zero. + if (recipient.code.length > 0 || uint256(uint160(recipient)) <= 18 || recipient == msg.sender) return; + + amount = bound(amount, 0, address(this).balance); + + SafeTransferLib.safeTransferETH(recipient, amount); + } + + function testFailFuzzTransferWithReturnsFalse( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(returnsFalse), to, amount); + } + + function testFailFuzzTransferWithReverting( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(reverting), to, amount); + } + + function testFailFuzzTransferWithReturnsTooLittle( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(returnsTooLittle), to, amount); + } + + function testFailFuzzTransferWithReturnsTwo( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransfer(address(returnsTwo), to, amount); + } + + function testFailFuzzTransferWithGarbage( + address to, + uint256 amount, + bytes memory garbage, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + require(garbage.length != 0 && (garbage.length < 32 || garbage[31] != bytes1(0x01))); + + returnsGarbage.setGarbage(garbage); + + verifySafeTransfer(address(returnsGarbage), to, amount); + } + + function testFailFuzzTransferFromWithReturnsFalse( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(returnsFalse), from, to, amount); + } + + function testFailFuzzTransferFromWithReverting( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(reverting), from, to, amount); + } + + function testFailFuzzTransferFromWithReturnsTooLittle( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(returnsTooLittle), from, to, amount); + } + + function testFailFuzzTransferFromWithReturnsTwo( + address from, + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeTransferFrom(address(returnsTwo), from, to, amount); + } + + function testFailFuzzTransferFromWithGarbage( + address from, + address to, + uint256 amount, + bytes memory garbage, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + require(garbage.length != 0 && (garbage.length < 32 || garbage[31] != bytes1(0x01))); + + returnsGarbage.setGarbage(garbage); + + verifySafeTransferFrom(address(returnsGarbage), from, to, amount); + } + + function testFailFuzzApproveWithReturnsFalse( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(returnsFalse), to, amount); + } + + function testFailFuzzApproveWithReverting( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(reverting), to, amount); + } + + function testFailFuzzApproveWithReturnsTooLittle( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(returnsTooLittle), to, amount); + } + + function testFailFuzzApproveWithReturnsTwo( + address to, + uint256 amount, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + verifySafeApprove(address(returnsTwo), to, amount); + } + + function testFailFuzzApproveWithGarbage( + address to, + uint256 amount, + bytes memory garbage, + bytes calldata brutalizeWith + ) public brutalizeMemory(brutalizeWith) { + require(garbage.length != 0 && (garbage.length < 32 || garbage[31] != bytes1(0x01))); + + returnsGarbage.setGarbage(garbage); + + verifySafeApprove(address(returnsGarbage), to, amount); + } + + function testFailFuzzTransferETHToContractWithoutFallback(uint256 amount, bytes calldata brutalizeWith) + public + brutalizeMemory(brutalizeWith) + { + SafeTransferLib.safeTransferETH(address(this), amount); + } + + function verifySafeTransfer( + address token, + address to, + uint256 amount + ) internal { + uint256 preBal = ERC20(token).balanceOf(to); + SafeTransferLib.safeTransfer(ERC20(address(token)), to, amount); + uint256 postBal = ERC20(token).balanceOf(to); + + if (to == address(this)) { + assertEq(preBal, postBal); + } else { + assertEq(postBal - preBal, amount); + } + } + + function verifySafeTransferFrom( + address token, + address from, + address to, + uint256 amount + ) internal { + forceApprove(token, from, address(this), amount); + + // We cast to MissingReturnToken here because it won't check + // that there was return data, which accommodates all tokens. + MissingReturnToken(token).transfer(from, amount); + + uint256 preBal = ERC20(token).balanceOf(to); + SafeTransferLib.safeTransferFrom(ERC20(token), from, to, amount); + uint256 postBal = ERC20(token).balanceOf(to); + + if (from == to) { + assertEq(preBal, postBal); + } else { + assertEq(postBal - preBal, amount); + } + } + + function verifySafeApprove( + address token, + address to, + uint256 amount + ) internal { + SafeTransferLib.safeApprove(ERC20(address(token)), to, amount); + + assertEq(ERC20(token).allowance(address(this), to), amount); + } + + function forceApprove( + address token, + address from, + address to, + uint256 amount + ) internal { + uint256 slot = token == address(erc20) ? 4 : 2; // Standard ERC20 name and symbol aren't constant. + + hevm.store( + token, + keccak256(abi.encode(to, keccak256(abi.encode(from, uint256(slot))))), + bytes32(uint256(amount)) + ); + + assertEq(ERC20(token).allowance(from, to), amount, "wrong allowance"); + } +} diff --git a/lib/solmate/src/test/WETH.t.sol b/lib/solmate/src/test/WETH.t.sol new file mode 100644 index 0000000..8bcb86b --- /dev/null +++ b/lib/solmate/src/test/WETH.t.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import {DSTestPlus} from "./utils/DSTestPlus.sol"; +import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; + +import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; + +import {WETH} from "../tokens/WETH.sol"; + +contract WETHTest is DSTestPlus { + WETH weth; + + function setUp() public { + weth = new WETH(); + } + + function testDeposit() public { + assertEq(weth.balanceOf(address(this)), 0); + assertEq(weth.totalSupply(), 0); + + SafeTransferLib.safeTransferETH(address(weth), 1 ether); + + assertEq(weth.balanceOf(address(this)), 1 ether); + assertEq(weth.totalSupply(), 1 ether); + } + + function testFallbackDeposit() public { + assertEq(weth.balanceOf(address(this)), 0); + assertEq(weth.totalSupply(), 0); + + weth.deposit{value: 1 ether}(); + + assertEq(weth.balanceOf(address(this)), 1 ether); + assertEq(weth.totalSupply(), 1 ether); + } + + function testWithdraw() public { + uint256 startingBalance = address(this).balance; + + weth.deposit{value: 1 ether}(); + + weth.withdraw(1 ether); + + uint256 balanceAfterWithdraw = address(this).balance; + + assertEq(balanceAfterWithdraw, startingBalance); + assertEq(weth.balanceOf(address(this)), 0); + assertEq(weth.totalSupply(), 0); + } + + function testPartialWithdraw() public { + weth.deposit{value: 1 ether}(); + + uint256 balanceBeforeWithdraw = address(this).balance; + + weth.withdraw(0.5 ether); + + uint256 balanceAfterWithdraw = address(this).balance; + + assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + 0.5 ether); + assertEq(weth.balanceOf(address(this)), 0.5 ether); + assertEq(weth.totalSupply(), 0.5 ether); + } + + function testFuzzDeposit(uint256 amount) public { + amount = bound(amount, 0, address(this).balance); + + assertEq(weth.balanceOf(address(this)), 0); + assertEq(weth.totalSupply(), 0); + + SafeTransferLib.safeTransferETH(address(weth), amount); + + assertEq(weth.balanceOf(address(this)), amount); + assertEq(weth.totalSupply(), amount); + } + + function testFuzzFallbackDeposit(uint256 amount) public { + amount = bound(amount, 0, address(this).balance); + + assertEq(weth.balanceOf(address(this)), 0); + assertEq(weth.totalSupply(), 0); + + weth.deposit{value: amount}(); + + assertEq(weth.balanceOf(address(this)), amount); + assertEq(weth.totalSupply(), amount); + } + + function testFuzzWithdraw(uint256 depositAmount, uint256 withdrawAmount) public { + depositAmount = bound(depositAmount, 0, address(this).balance); + withdrawAmount = bound(withdrawAmount, 0, depositAmount); + + weth.deposit{value: depositAmount}(); + + uint256 balanceBeforeWithdraw = address(this).balance; + + weth.withdraw(withdrawAmount); + + uint256 balanceAfterWithdraw = address(this).balance; + + assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + withdrawAmount); + assertEq(weth.balanceOf(address(this)), depositAmount - withdrawAmount); + assertEq(weth.totalSupply(), depositAmount - withdrawAmount); + } + + receive() external payable {} +} + +contract WETHInvariants is DSTestPlus, DSInvariantTest { + WETHTester wethTester; + WETH weth; + + function setUp() public { + weth = new WETH(); + wethTester = new WETHTester{value: address(this).balance}(weth); + + addTargetContract(address(wethTester)); + } + + function invariantTotalSupplyEqualsBalance() public { + assertEq(address(weth).balance, weth.totalSupply()); + } +} + +contract WETHTester { + WETH weth; + + constructor(WETH _weth) payable { + weth = _weth; + } + + function deposit(uint256 amount) public { + weth.deposit{value: amount}(); + } + + function fallbackDeposit(uint256 amount) public { + SafeTransferLib.safeTransferETH(address(weth), amount); + } + + function withdraw(uint256 amount) public { + weth.withdraw(amount); + } + + receive() external payable {} +} diff --git a/lib/solmate/src/test/utils/DSInvariantTest.sol b/lib/solmate/src/test/utils/DSInvariantTest.sol new file mode 100644 index 0000000..531a9fd --- /dev/null +++ b/lib/solmate/src/test/utils/DSInvariantTest.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract DSInvariantTest { + address[] private targets; + + function targetContracts() public view virtual returns (address[] memory) { + require(targets.length > 0, "NO_TARGET_CONTRACTS"); + + return targets; + } + + function addTargetContract(address newTargetContract) internal virtual { + targets.push(newTargetContract); + } +} diff --git a/lib/solmate/src/test/utils/DSTestPlus.sol b/lib/solmate/src/test/utils/DSTestPlus.sol new file mode 100644 index 0000000..2ad6953 --- /dev/null +++ b/lib/solmate/src/test/utils/DSTestPlus.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {DSTest} from "ds-test/test.sol"; + +import {Hevm} from "./Hevm.sol"; + +/// @notice Extended testing framework for DappTools projects. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/test/utils/DSTestPlus.sol) +contract DSTestPlus is DSTest { + Hevm internal constant hevm = Hevm(HEVM_ADDRESS); + + address internal constant DEAD_ADDRESS = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; + + uint256 private checkpointGasLeft; + + modifier brutalizeMemory(bytes memory brutalizeWith) { + /// @solidity memory-safe-assembly + assembly { + // Fill the 64 bytes of scratch space with the data. + pop( + staticcall( + gas(), // Pass along all the gas in the call. + 0x04, // Call the identity precompile address. + brutalizeWith, // Offset is the bytes' pointer. + 64, // Copy enough to only fill the scratch space. + 0, // Store the return value in the scratch space. + 64 // Scratch space is only 64 bytes in size, we don't want to write further. + ) + ) + + let size := add(mload(brutalizeWith), 32) // Add 32 to include the 32 byte length slot. + + // Fill the free memory pointer's destination with the data. + pop( + staticcall( + gas(), // Pass along all the gas in the call. + 0x04, // Call the identity precompile address. + brutalizeWith, // Offset is the bytes' pointer. + size, // We want to pass the length of the bytes. + mload(0x40), // Store the return value at the free memory pointer. + size // Since the precompile just returns its input, we reuse size. + ) + ) + } + + _; + } + + function startMeasuringGas() internal virtual { + checkpointGasLeft = 1; // Start the slot warm. + + checkpointGasLeft = gasleft(); + } + + function stopMeasuringGas() internal virtual { + uint256 checkpointGasLeft2 = gasleft(); + + // Subtract 100 to account for the warm SSTORE in startMeasuringGas. + uint256 gasDelta = checkpointGasLeft - checkpointGasLeft2 - 100; + + emit log_named_uint("Gas Used", gasDelta); + } + + function fail(string memory err) internal virtual { + emit log_named_string("Error", err); + fail(); + } + + function assertFalse(bool data) internal virtual { + assertTrue(!data); + } + + function assertUint128Eq(uint128 a, uint128 b) internal virtual { + assertEq(uint256(a), uint256(b)); + } + + function assertUint64Eq(uint64 a, uint64 b) internal virtual { + assertEq(uint256(a), uint256(b)); + } + + function assertUint96Eq(uint96 a, uint96 b) internal virtual { + assertEq(uint256(a), uint256(b)); + } + + function assertUint32Eq(uint32 a, uint32 b) internal virtual { + assertEq(uint256(a), uint256(b)); + } + + function assertBoolEq(bool a, bool b) internal virtual { + b ? assertTrue(a) : assertFalse(a); + } + + function assertApproxEq( + uint256 a, + uint256 b, + uint256 maxDelta + ) internal virtual { + uint256 delta = a > b ? a - b : b - a; + + if (delta > maxDelta) { + emit log("Error: a ~= b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + emit log_named_uint(" Max Delta", maxDelta); + emit log_named_uint(" Delta", delta); + fail(); + } + } + + function assertApproxEq( + int256 a, + int256 b, + uint256 maxDelta + ) internal virtual { + int256 delta = a > b ? a - b : b - a; + + if (delta > int256(maxDelta)) { + emit log("Error: a ~= b not satisfied [uint]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + emit log_named_uint(" Max Delta", maxDelta); + emit log_named_int(" Delta", delta); + fail(); + } + } + + function assertRelApproxEq( + uint256 a, + uint256 b, + uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = ((a > b ? a - b : b - a) * 1e18) / b; + + if (percentDelta > maxPercentDelta) { + emit log("Error: a ~= b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint(" % Delta", percentDelta, 18); + fail(); + } + } + + function assertBytesEq(bytes memory a, bytes memory b) internal virtual { + if (keccak256(a) != keccak256(b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Expected", b); + emit log_named_bytes(" Actual", a); + fail(); + } + } + + function assertUintArrayEq(uint256[] memory a, uint256[] memory b) internal virtual { + require(a.length == b.length, "LENGTH_MISMATCH"); + + for (uint256 i = 0; i < a.length; i++) { + assertEq(a[i], b[i]); + } + } + + function bound( + uint256 x, + uint256 min, + uint256 max + ) internal returns (uint256 result) { + require(max >= min, "MAX_LESS_THAN_MIN"); + + uint256 size = max - min; + + if (size == 0) result = min; + else if (size == type(uint256).max) result = x; + else { + ++size; // Make max inclusive. + uint256 mod = x % size; + result = min + mod; + } + + emit log_named_uint("Bound Result", result); + } + + function min3( + uint256 a, + uint256 b, + uint256 c + ) internal pure returns (uint256) { + return a > b ? (b > c ? c : b) : (a > c ? c : a); + } + + function min2(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b ? b : a; + } +} diff --git a/lib/solmate/src/test/utils/Hevm.sol b/lib/solmate/src/test/utils/Hevm.sol new file mode 100644 index 0000000..20f5a40 --- /dev/null +++ b/lib/solmate/src/test/utils/Hevm.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +interface Hevm { + /// @notice Sets the block timestamp. + function warp(uint256) external; + + /// @notice Sets the block height. + function roll(uint256) external; + + /// @notice Sets the block base fee. + function fee(uint256) external; + + /// @notice Loads a storage slot from an address. + function load(address, bytes32) external returns (bytes32); + + /// @notice Stores a value to an address' storage slot. + function store( + address, + bytes32, + bytes32 + ) external; + + /// @notice Signs a digest with a private key, returns v r s. + function sign(uint256, bytes32) + external + returns ( + uint8, + bytes32, + bytes32 + ); + + /// @notice Gets address for a given private key. + function addr(uint256) external returns (address); + + /// @notice Performs a foreign function call via a terminal call. + function ffi(string[] calldata) external returns (bytes memory); + + /// @notice Sets the next call's msg.sender to be the input address. + function prank(address) external; + + /// @notice Sets all subsequent calls' msg.sender to be the input address until stopPrank is called. + function startPrank(address) external; + + /// @notice Sets the next call's msg.sender to be the input address and the tx.origin to be the second input. + function prank(address, address) external; + + /// @notice Sets all subsequent calls' msg.sender to be the input address and + /// sets tx.origin to be the second address inputted until stopPrank is called. + function startPrank(address, address) external; + + /// @notice Resets msg.sender to its original value before a prank. + function stopPrank() external; + + /// @notice Sets an address' balance. + function deal(address, uint256) external; + + /// @notice Sets an address' code. + function etch(address, bytes calldata) external; + + /// @notice Expects an error from the next call. + function expectRevert(bytes calldata) external; + + /// @notice Expects a revert from the next call. + function expectRevert(bytes4) external; + + /// @notice Record all storage reads and writes. + function record() external; + + /// @notice Gets all accessed reads and write slots from a recording session, for a given address. + function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes); + + /// @notice Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). + /// @notice Call this function, then emit an event, then call a function. Internally after the call, we check + /// if logs were emitted in the expected order with the expected topics and data as specified by the booleans. + function expectEmit( + bool, + bool, + bool, + bool + ) external; + + /// @notice Mocks the behavior of a contract call, setting the input and output for a function. + /// @notice Calldata can either be strict or a partial match, e.g. if only passed + /// a selector to the expected calldata, then the entire function will be mocked. + function mockCall( + address, + bytes calldata, + bytes calldata + ) external; + + /// @notice Clears all mocked calls. + function clearMockedCalls() external; + + /// @notice Expect a call to an address with the specified calldata. + /// @notice Calldata can either be strict or a partial match. + function expectCall(address, bytes calldata) external; + + /// @notice Fetches the contract bytecode from its artifact file. + function getCode(string calldata) external returns (bytes memory); + + /// @notice Label an address in test traces. + function label(address addr, string calldata label) external; + + /// @notice When fuzzing, generate new inputs if the input conditional is not met. + function assume(bool) external; +} diff --git a/lib/solmate/src/test/utils/mocks/MockAuthChild.sol b/lib/solmate/src/test/utils/mocks/MockAuthChild.sol new file mode 100644 index 0000000..df0deb5 --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockAuthChild.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {Auth, Authority} from "../../../auth/Auth.sol"; + +contract MockAuthChild is Auth(msg.sender, Authority(address(0))) { + bool public flag; + + function updateFlag() public virtual requiresAuth { + flag = true; + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockAuthority.sol b/lib/solmate/src/test/utils/mocks/MockAuthority.sol new file mode 100644 index 0000000..c8251db --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockAuthority.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {Authority} from "../../../auth/Auth.sol"; + +contract MockAuthority is Authority { + bool immutable allowCalls; + + constructor(bool _allowCalls) { + allowCalls = _allowCalls; + } + + function canCall( + address, + address, + bytes4 + ) public view override returns (bool) { + return allowCalls; + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockERC1155.sol b/lib/solmate/src/test/utils/mocks/MockERC1155.sol new file mode 100644 index 0000000..a3228ef --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockERC1155.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC1155} from "../../../tokens/ERC1155.sol"; + +contract MockERC1155 is ERC1155 { + function uri(uint256) public pure virtual override returns (string memory) {} + + function mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual { + _mint(to, id, amount, data); + } + + function batchMint( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual { + _batchMint(to, ids, amounts, data); + } + + function burn( + address from, + uint256 id, + uint256 amount + ) public virtual { + _burn(from, id, amount); + } + + function batchBurn( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) public virtual { + _batchBurn(from, ids, amounts); + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockERC1155B.sol b/lib/solmate/src/test/utils/mocks/MockERC1155B.sol new file mode 100644 index 0000000..29a97db --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockERC1155B.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC1155B} from "../../../tokens/ERC1155B.sol"; + +contract MockERC1155B is ERC1155B { + function uri(uint256) public pure virtual override returns (string memory) {} + + function mint( + address to, + uint256 id, + bytes memory data + ) public virtual { + _mint(to, id, data); + } + + function batchMint( + address to, + uint256[] memory ids, + bytes memory data + ) public virtual { + _batchMint(to, ids, data); + } + + function burn(uint256 id) public virtual { + _burn(id); + } + + function batchBurn(address from, uint256[] memory ids) public virtual { + _batchBurn(from, ids); + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockERC20.sol b/lib/solmate/src/test/utils/mocks/MockERC20.sol new file mode 100644 index 0000000..35fc648 --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockERC20.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC20} from "../../../tokens/ERC20.sol"; + +contract MockERC20 is ERC20 { + constructor( + string memory _name, + string memory _symbol, + uint8 _decimals + ) ERC20(_name, _symbol, _decimals) {} + + function mint(address to, uint256 value) public virtual { + _mint(to, value); + } + + function burn(address from, uint256 value) public virtual { + _burn(from, value); + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockERC4626.sol b/lib/solmate/src/test/utils/mocks/MockERC4626.sol new file mode 100644 index 0000000..bfa9790 --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockERC4626.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC20} from "../../../tokens/ERC20.sol"; +import {ERC4626} from "../../../mixins/ERC4626.sol"; + +contract MockERC4626 is ERC4626 { + uint256 public beforeWithdrawHookCalledCounter = 0; + uint256 public afterDepositHookCalledCounter = 0; + + constructor( + ERC20 _underlying, + string memory _name, + string memory _symbol + ) ERC4626(_underlying, _name, _symbol) {} + + function totalAssets() public view override returns (uint256) { + return asset.balanceOf(address(this)); + } + + function beforeWithdraw(uint256, uint256) internal override { + beforeWithdrawHookCalledCounter++; + } + + function afterDeposit(uint256, uint256) internal override { + afterDepositHookCalledCounter++; + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockERC721.sol b/lib/solmate/src/test/utils/mocks/MockERC721.sol new file mode 100644 index 0000000..7833a47 --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockERC721.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC721} from "../../../tokens/ERC721.sol"; + +contract MockERC721 is ERC721 { + constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {} + + function tokenURI(uint256) public pure virtual override returns (string memory) {} + + function mint(address to, uint256 tokenId) public virtual { + _mint(to, tokenId); + } + + function burn(uint256 tokenId) public virtual { + _burn(tokenId); + } + + function safeMint(address to, uint256 tokenId) public virtual { + _safeMint(to, tokenId); + } + + function safeMint( + address to, + uint256 tokenId, + bytes memory data + ) public virtual { + _safeMint(to, tokenId, data); + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockLibBitmap.sol b/lib/solmate/src/test/utils/mocks/MockLibBitmap.sol new file mode 100644 index 0000000..d981e62 --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockLibBitmap.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {LibBitmap} from "../../../utils/LibBitmap.sol"; + +contract MockLibBitmap { + using LibBitmap for LibBitmap.Bitmap; + LibBitmap.Bitmap bitmap; + + function get(uint256 index) public view returns (bool result) { + result = bitmap.get(index); + } + + function set(uint256 index) public { + bitmap.set(index); + } + + function unset(uint256 index) public { + bitmap.unset(index); + } + + function setTo(uint256 index, bool shouldSet) public { + bitmap.setTo(index, shouldSet); + } +} diff --git a/lib/solmate/src/test/utils/mocks/MockOwned.sol b/lib/solmate/src/test/utils/mocks/MockOwned.sol new file mode 100644 index 0000000..52ef918 --- /dev/null +++ b/lib/solmate/src/test/utils/mocks/MockOwned.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +import {Owned} from "../../../auth/Owned.sol"; + +contract MockOwned is Owned(msg.sender) { + bool public flag; + + function updateFlag() public virtual onlyOwner { + flag = true; + } +} diff --git a/lib/solmate/src/test/utils/users/ERC1155BUser.sol b/lib/solmate/src/test/utils/users/ERC1155BUser.sol new file mode 100644 index 0000000..5ddcd76 --- /dev/null +++ b/lib/solmate/src/test/utils/users/ERC1155BUser.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC1155TokenReceiver} from "../../../tokens/ERC1155.sol"; +import {ERC1155B} from "../../../tokens/ERC1155B.sol"; + +contract ERC1155BUser is ERC1155TokenReceiver { + ERC1155B token; + + constructor(ERC1155B _token) { + token = _token; + } + + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external virtual override returns (bytes4) { + return ERC1155TokenReceiver.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external virtual override returns (bytes4) { + return ERC1155TokenReceiver.onERC1155BatchReceived.selector; + } + + function setApprovalForAll(address operator, bool approved) public virtual { + token.setApprovalForAll(operator, approved); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual { + token.safeTransferFrom(from, to, id, amount, data); + } + + function safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual { + token.safeBatchTransferFrom(from, to, ids, amounts, data); + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol b/lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol new file mode 100644 index 0000000..e295912 --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract MissingReturnToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "MissingReturnToken"; + + string public constant symbol = "MRT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 amount) public virtual { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + } + + function transfer(address to, uint256 amount) public virtual { + balanceOf[msg.sender] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual { + uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol new file mode 100644 index 0000000..2e41fb2 --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract ReturnsFalseToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "ReturnsFalseToken"; + + string public constant symbol = "RFT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address, uint256) public virtual returns (bool) { + return false; + } + + function transfer(address, uint256) public virtual returns (bool) { + return false; + } + + function transferFrom( + address, + address, + uint256 + ) public virtual returns (bool) { + return false; + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol new file mode 100644 index 0000000..5fbfa75 --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract ReturnsGarbageToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "ReturnsGarbageToken"; + + string public constant symbol = "RGT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + MOCK STORAGE + //////////////////////////////////////////////////////////////*/ + + bytes garbage; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 amount) public virtual { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + + bytes memory _garbage = garbage; + + assembly { + return(add(_garbage, 32), mload(_garbage)) + } + } + + function transfer(address to, uint256 amount) public virtual { + balanceOf[msg.sender] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + + bytes memory _garbage = garbage; + + assembly { + return(add(_garbage, 32), mload(_garbage)) + } + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual { + uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + + bytes memory _garbage = garbage; + + assembly { + return(add(_garbage, 32), mload(_garbage)) + } + } + + /*/////////////////////////////////////////////////////////////// + MOCK LOGIC + //////////////////////////////////////////////////////////////*/ + + function setGarbage(bytes memory _garbage) public virtual { + garbage = _garbage; + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol new file mode 100644 index 0000000..0bf7a7b --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract ReturnsTooLittleToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "ReturnsTooLittleToken"; + + string public constant symbol = "RTLT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address, uint256) public virtual { + assembly { + mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) + return(0, 8) + } + } + + function transfer(address, uint256) public virtual { + assembly { + mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) + return(0, 8) + } + } + + function transferFrom( + address, + address, + uint256 + ) public virtual { + assembly { + mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) + return(0, 8) + } + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol new file mode 100644 index 0000000..6b21c2a --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract ReturnsTooMuchToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "ReturnsTooMuchToken"; + + string public constant symbol = "RTMT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 amount) public virtual { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + + assembly { + mstore(0, 1) + return(0, 4096) + } + } + + function transfer(address to, uint256 amount) public virtual { + balanceOf[msg.sender] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + + assembly { + mstore(0, 1) + return(0, 4096) + } + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual { + uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + + assembly { + mstore(0, 1) + return(0, 4096) + } + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol new file mode 100644 index 0000000..9af6eee --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract ReturnsTwoToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "ReturnsFalseToken"; + + string public constant symbol = "RTT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address, uint256) public virtual returns (uint256) { + return 2; + } + + function transfer(address, uint256) public virtual returns (uint256) { + return 2; + } + + function transferFrom( + address, + address, + uint256 + ) public virtual returns (uint256) { + return 2; + } +} diff --git a/lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol b/lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol new file mode 100644 index 0000000..6ebac86 --- /dev/null +++ b/lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract RevertingToken { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public constant name = "RevertingToken"; + + string public constant symbol = "RT"; + + uint8 public constant decimals = 18; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor() { + totalSupply = type(uint256).max; + balanceOf[msg.sender] = type(uint256).max; + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address, uint256) public virtual { + revert(); + } + + function transfer(address, uint256) public virtual { + revert(); + } + + function transferFrom( + address, + address, + uint256 + ) public virtual { + revert(); + } +} diff --git a/lib/solmate/src/tokens/ERC1155.sol b/lib/solmate/src/tokens/ERC1155.sol new file mode 100644 index 0000000..603cac6 --- /dev/null +++ b/lib/solmate/src/tokens/ERC1155.sol @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Minimalist and gas efficient standard ERC1155 implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) +abstract contract ERC1155 { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event TransferSingle( + address indexed operator, + address indexed from, + address indexed to, + uint256 id, + uint256 amount + ); + + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] amounts + ); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + event URI(string value, uint256 indexed id); + + /*////////////////////////////////////////////////////////////// + ERC1155 STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => mapping(uint256 => uint256)) public balanceOf; + + mapping(address => mapping(address => bool)) public isApprovedForAll; + + /*////////////////////////////////////////////////////////////// + METADATA LOGIC + //////////////////////////////////////////////////////////////*/ + + function uri(uint256 id) public view virtual returns (string memory); + + /*////////////////////////////////////////////////////////////// + ERC1155 LOGIC + //////////////////////////////////////////////////////////////*/ + + function setApprovalForAll(address operator, bool approved) public virtual { + isApprovedForAll[msg.sender][operator] = approved; + + emit ApprovalForAll(msg.sender, operator, approved); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes calldata data + ) public virtual { + require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); + + balanceOf[from][id] -= amount; + balanceOf[to][id] += amount; + + emit TransferSingle(msg.sender, from, to, id, amount); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == + ERC1155TokenReceiver.onERC1155Received.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) public virtual { + require(ids.length == amounts.length, "LENGTH_MISMATCH"); + + require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); + + // Storing these outside the loop saves ~15 gas per iteration. + uint256 id; + uint256 amount; + + for (uint256 i = 0; i < ids.length; ) { + id = ids[i]; + amount = amounts[i]; + + balanceOf[from][id] -= amount; + balanceOf[to][id] += amount; + + // An array can't have a total length + // larger than the max uint256 value. + unchecked { + ++i; + } + } + + emit TransferBatch(msg.sender, from, to, ids, amounts); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == + ERC1155TokenReceiver.onERC1155BatchReceived.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) + public + view + virtual + returns (uint256[] memory balances) + { + require(owners.length == ids.length, "LENGTH_MISMATCH"); + + balances = new uint256[](owners.length); + + // Unchecked because the only math done is incrementing + // the array index counter which cannot possibly overflow. + unchecked { + for (uint256 i = 0; i < owners.length; ++i) { + balances[i] = balanceOf[owners[i]][ids[i]]; + } + } + } + + /*////////////////////////////////////////////////////////////// + ERC165 LOGIC + //////////////////////////////////////////////////////////////*/ + + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return + interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 + interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 + interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI + } + + /*////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + balanceOf[to][id] += amount; + + emit TransferSingle(msg.sender, address(0), to, id, amount); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == + ERC1155TokenReceiver.onERC1155Received.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function _batchMint( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + uint256 idsLength = ids.length; // Saves MLOADs. + + require(idsLength == amounts.length, "LENGTH_MISMATCH"); + + for (uint256 i = 0; i < idsLength; ) { + balanceOf[to][ids[i]] += amounts[i]; + + // An array can't have a total length + // larger than the max uint256 value. + unchecked { + ++i; + } + } + + emit TransferBatch(msg.sender, address(0), to, ids, amounts); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == + ERC1155TokenReceiver.onERC1155BatchReceived.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function _batchBurn( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) internal virtual { + uint256 idsLength = ids.length; // Saves MLOADs. + + require(idsLength == amounts.length, "LENGTH_MISMATCH"); + + for (uint256 i = 0; i < idsLength; ) { + balanceOf[from][ids[i]] -= amounts[i]; + + // An array can't have a total length + // larger than the max uint256 value. + unchecked { + ++i; + } + } + + emit TransferBatch(msg.sender, from, address(0), ids, amounts); + } + + function _burn( + address from, + uint256 id, + uint256 amount + ) internal virtual { + balanceOf[from][id] -= amount; + + emit TransferSingle(msg.sender, from, address(0), id, amount); + } +} + +/// @notice A generic interface for a contract which properly accepts ERC1155 tokens. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) +abstract contract ERC1155TokenReceiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes calldata + ) external virtual returns (bytes4) { + return ERC1155TokenReceiver.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external virtual returns (bytes4) { + return ERC1155TokenReceiver.onERC1155BatchReceived.selector; + } +} diff --git a/lib/solmate/src/tokens/ERC1155B.sol b/lib/solmate/src/tokens/ERC1155B.sol new file mode 100644 index 0000000..c56e447 --- /dev/null +++ b/lib/solmate/src/tokens/ERC1155B.sol @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC1155TokenReceiver} from "./ERC1155.sol"; + +/// @notice Minimalist and gas efficient ERC1155 implementation optimized for single supply ids. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155B.sol) +abstract contract ERC1155B { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event TransferSingle( + address indexed operator, + address indexed from, + address indexed to, + uint256 id, + uint256 amount + ); + + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] amounts + ); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + event URI(string value, uint256 indexed id); + + /*////////////////////////////////////////////////////////////// + ERC1155 STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => mapping(address => bool)) public isApprovedForAll; + + /*////////////////////////////////////////////////////////////// + ERC1155B STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 => address) public ownerOf; + + function balanceOf(address owner, uint256 id) public view virtual returns (uint256 bal) { + address idOwner = ownerOf[id]; + + assembly { + // We avoid branching by using assembly to take + // the bool output of eq() and use it as a uint. + bal := eq(idOwner, owner) + } + } + + /*////////////////////////////////////////////////////////////// + METADATA LOGIC + //////////////////////////////////////////////////////////////*/ + + function uri(uint256 id) public view virtual returns (string memory); + + /*////////////////////////////////////////////////////////////// + ERC1155 LOGIC + //////////////////////////////////////////////////////////////*/ + + function setApprovalForAll(address operator, bool approved) public virtual { + isApprovedForAll[msg.sender][operator] = approved; + + emit ApprovalForAll(msg.sender, operator, approved); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes calldata data + ) public virtual { + require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); + + require(from == ownerOf[id], "WRONG_FROM"); // Can only transfer from the owner. + + // Can only transfer 1 with ERC1155B. + require(amount == 1, "INVALID_AMOUNT"); + + ownerOf[id] = to; + + emit TransferSingle(msg.sender, from, to, id, amount); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == + ERC1155TokenReceiver.onERC1155Received.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) public virtual { + require(ids.length == amounts.length, "LENGTH_MISMATCH"); + + require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); + + // Storing these outside the loop saves ~15 gas per iteration. + uint256 id; + uint256 amount; + + // Unchecked because the only math done is incrementing + // the array index counter which cannot possibly overflow. + unchecked { + for (uint256 i = 0; i < ids.length; i++) { + id = ids[i]; + amount = amounts[i]; + + // Can only transfer from the owner. + require(from == ownerOf[id], "WRONG_FROM"); + + // Can only transfer 1 with ERC1155B. + require(amount == 1, "INVALID_AMOUNT"); + + ownerOf[id] = to; + } + } + + emit TransferBatch(msg.sender, from, to, ids, amounts); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == + ERC1155TokenReceiver.onERC1155BatchReceived.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) + public + view + virtual + returns (uint256[] memory balances) + { + require(owners.length == ids.length, "LENGTH_MISMATCH"); + + balances = new uint256[](owners.length); + + // Unchecked because the only math done is incrementing + // the array index counter which cannot possibly overflow. + unchecked { + for (uint256 i = 0; i < owners.length; ++i) { + balances[i] = balanceOf(owners[i], ids[i]); + } + } + } + + /*////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint( + address to, + uint256 id, + bytes memory data + ) internal virtual { + // Minting twice would effectively be a force transfer. + require(ownerOf[id] == address(0), "ALREADY_MINTED"); + + ownerOf[id] = to; + + emit TransferSingle(msg.sender, address(0), to, id, 1); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, 1, data) == + ERC1155TokenReceiver.onERC1155Received.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function _batchMint( + address to, + uint256[] memory ids, + bytes memory data + ) internal virtual { + uint256 idsLength = ids.length; // Saves MLOADs. + + // Generate an amounts array locally to use in the event below. + uint256[] memory amounts = new uint256[](idsLength); + + uint256 id; // Storing outside the loop saves ~7 gas per iteration. + + // Unchecked because the only math done is incrementing + // the array index counter which cannot possibly overflow. + unchecked { + for (uint256 i = 0; i < idsLength; ++i) { + id = ids[i]; + + // Minting twice would effectively be a force transfer. + require(ownerOf[id] == address(0), "ALREADY_MINTED"); + + ownerOf[id] = to; + + amounts[i] = 1; + } + } + + emit TransferBatch(msg.sender, address(0), to, ids, amounts); + + if (to.code.length != 0) { + require( + ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == + ERC1155TokenReceiver.onERC1155BatchReceived.selector, + "UNSAFE_RECIPIENT" + ); + } else require(to != address(0), "INVALID_RECIPIENT"); + } + + function _batchBurn(address from, uint256[] memory ids) internal virtual { + // Burning unminted tokens makes no sense. + require(from != address(0), "INVALID_FROM"); + + uint256 idsLength = ids.length; // Saves MLOADs. + + // Generate an amounts array locally to use in the event below. + uint256[] memory amounts = new uint256[](idsLength); + + uint256 id; // Storing outside the loop saves ~7 gas per iteration. + + // Unchecked because the only math done is incrementing + // the array index counter which cannot possibly overflow. + unchecked { + for (uint256 i = 0; i < idsLength; ++i) { + id = ids[i]; + + require(ownerOf[id] == from, "WRONG_FROM"); + + ownerOf[id] = address(0); + + amounts[i] = 1; + } + } + + emit TransferBatch(msg.sender, from, address(0), ids, amounts); + } + + function _burn(uint256 id) internal virtual { + address owner = ownerOf[id]; + + require(owner != address(0), "NOT_MINTED"); + + ownerOf[id] = address(0); + + emit TransferSingle(msg.sender, owner, address(0), id, 1); + } +} diff --git a/lib/solmate/src/tokens/ERC20.sol b/lib/solmate/src/tokens/ERC20.sol new file mode 100644 index 0000000..d6f5194 --- /dev/null +++ b/lib/solmate/src/tokens/ERC20.sol @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) +/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) +/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. +abstract contract ERC20 { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public name; + + string public symbol; + + uint8 public immutable decimals; + + /*////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*////////////////////////////////////////////////////////////// + EIP-2612 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 internal immutable INITIAL_CHAIN_ID; + + bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; + + mapping(address => uint256) public nonces; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor( + string memory _name, + string memory _symbol, + uint8 _decimals + ) { + name = _name; + symbol = _symbol; + decimals = _decimals; + + INITIAL_CHAIN_ID = block.chainid; + INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); + } + + /*////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 amount) public virtual returns (bool) { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + + return true; + } + + function transfer(address to, uint256 amount) public virtual returns (bool) { + balanceOf[msg.sender] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + + return true; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual returns (bool) { + uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + + return true; + } + + /*////////////////////////////////////////////////////////////// + EIP-2612 LOGIC + //////////////////////////////////////////////////////////////*/ + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual { + require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); + + // Unchecked because the only math done is incrementing + // the owner's nonce which cannot realistically overflow. + unchecked { + address recoveredAddress = ecrecover( + keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ), + owner, + spender, + value, + nonces[owner]++, + deadline + ) + ) + ) + ), + v, + r, + s + ); + + require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); + + allowance[recoveredAddress][spender] = value; + } + + emit Approval(owner, spender, value); + } + + function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { + return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); + } + + function computeDomainSeparator() internal view virtual returns (bytes32) { + return + keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes(name)), + keccak256("1"), + block.chainid, + address(this) + ) + ); + } + + /*////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint(address to, uint256 amount) internal virtual { + totalSupply += amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(address(0), to, amount); + } + + function _burn(address from, uint256 amount) internal virtual { + balanceOf[from] -= amount; + + // Cannot underflow because a user's balance + // will never be larger than the total supply. + unchecked { + totalSupply -= amount; + } + + emit Transfer(from, address(0), amount); + } +} diff --git a/lib/solmate/src/tokens/ERC721.sol b/lib/solmate/src/tokens/ERC721.sol new file mode 100644 index 0000000..15bdb04 --- /dev/null +++ b/lib/solmate/src/tokens/ERC721.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Modern, minimalist, and gas efficient ERC-721 implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) +abstract contract ERC721 { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 indexed id); + + event Approval(address indexed owner, address indexed spender, uint256 indexed id); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /*////////////////////////////////////////////////////////////// + METADATA STORAGE/LOGIC + //////////////////////////////////////////////////////////////*/ + + string public name; + + string public symbol; + + function tokenURI(uint256 id) public view virtual returns (string memory); + + /*////////////////////////////////////////////////////////////// + ERC721 BALANCE/OWNER STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 => address) internal _ownerOf; + + mapping(address => uint256) internal _balanceOf; + + function ownerOf(uint256 id) public view virtual returns (address owner) { + require((owner = _ownerOf[id]) != address(0), "NOT_MINTED"); + } + + function balanceOf(address owner) public view virtual returns (uint256) { + require(owner != address(0), "ZERO_ADDRESS"); + + return _balanceOf[owner]; + } + + /*////////////////////////////////////////////////////////////// + ERC721 APPROVAL STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 => address) public getApproved; + + mapping(address => mapping(address => bool)) public isApprovedForAll; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(string memory _name, string memory _symbol) { + name = _name; + symbol = _symbol; + } + + /*////////////////////////////////////////////////////////////// + ERC721 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 id) public virtual { + address owner = _ownerOf[id]; + + require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); + + getApproved[id] = spender; + + emit Approval(owner, spender, id); + } + + function setApprovalForAll(address operator, bool approved) public virtual { + isApprovedForAll[msg.sender][operator] = approved; + + emit ApprovalForAll(msg.sender, operator, approved); + } + + function transferFrom( + address from, + address to, + uint256 id + ) public virtual { + require(from == _ownerOf[id], "WRONG_FROM"); + + require(to != address(0), "INVALID_RECIPIENT"); + + require( + msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], + "NOT_AUTHORIZED" + ); + + // Underflow of the sender's balance is impossible because we check for + // ownership above and the recipient's balance can't realistically overflow. + unchecked { + _balanceOf[from]--; + + _balanceOf[to]++; + } + + _ownerOf[id] = to; + + delete getApproved[id]; + + emit Transfer(from, to, id); + } + + function safeTransferFrom( + address from, + address to, + uint256 id + ) public virtual { + transferFrom(from, to, id); + + if (to.code.length != 0) + require( + ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + bytes calldata data + ) public virtual { + transferFrom(from, to, id); + + if (to.code.length != 0) + require( + ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + /*////////////////////////////////////////////////////////////// + ERC165 LOGIC + //////////////////////////////////////////////////////////////*/ + + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return + interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 + interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 + interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata + } + + /*////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint(address to, uint256 id) internal virtual { + require(to != address(0), "INVALID_RECIPIENT"); + + require(_ownerOf[id] == address(0), "ALREADY_MINTED"); + + // Counter overflow is incredibly unrealistic. + unchecked { + _balanceOf[to]++; + } + + _ownerOf[id] = to; + + emit Transfer(address(0), to, id); + } + + function _burn(uint256 id) internal virtual { + address owner = _ownerOf[id]; + + require(owner != address(0), "NOT_MINTED"); + + // Ownership check above ensures no underflow. + unchecked { + _balanceOf[owner]--; + } + + delete _ownerOf[id]; + + delete getApproved[id]; + + emit Transfer(owner, address(0), id); + } + + /*////////////////////////////////////////////////////////////// + INTERNAL SAFE MINT LOGIC + //////////////////////////////////////////////////////////////*/ + + function _safeMint(address to, uint256 id) internal virtual { + _mint(to, id); + + if (to.code.length != 0) + require( + ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + function _safeMint( + address to, + uint256 id, + bytes memory data + ) internal virtual { + _mint(to, id); + + if (to.code.length != 0) + require( + ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } +} + +/// @notice A generic interface for a contract which properly accepts ERC721 tokens. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) +abstract contract ERC721TokenReceiver { + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) external virtual returns (bytes4) { + return ERC721TokenReceiver.onERC721Received.selector; + } +} diff --git a/lib/solmate/src/tokens/WETH.sol b/lib/solmate/src/tokens/WETH.sol new file mode 100644 index 0000000..8d57287 --- /dev/null +++ b/lib/solmate/src/tokens/WETH.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC20} from "./ERC20.sol"; + +import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; + +/// @notice Minimalist and modern Wrapped Ether implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/WETH.sol) +/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol) +contract WETH is ERC20("Wrapped Ether", "WETH", 18) { + using SafeTransferLib for address; + + event Deposit(address indexed from, uint256 amount); + + event Withdrawal(address indexed to, uint256 amount); + + function deposit() public payable virtual { + _mint(msg.sender, msg.value); + + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 amount) public virtual { + _burn(msg.sender, amount); + + emit Withdrawal(msg.sender, amount); + + msg.sender.safeTransferETH(amount); + } + + receive() external payable virtual { + deposit(); + } +} diff --git a/lib/solmate/src/utils/Bytes32AddressLib.sol b/lib/solmate/src/utils/Bytes32AddressLib.sol new file mode 100644 index 0000000..dea8e96 --- /dev/null +++ b/lib/solmate/src/utils/Bytes32AddressLib.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Library for converting between addresses and bytes32 values. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/Bytes32AddressLib.sol) +library Bytes32AddressLib { + function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { + return address(uint160(uint256(bytesValue))); + } + + function fillLast12Bytes(address addressValue) internal pure returns (bytes32) { + return bytes32(bytes20(addressValue)); + } +} diff --git a/lib/solmate/src/utils/CREATE3.sol b/lib/solmate/src/utils/CREATE3.sol new file mode 100644 index 0000000..d0c4e4c --- /dev/null +++ b/lib/solmate/src/utils/CREATE3.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {Bytes32AddressLib} from "./Bytes32AddressLib.sol"; + +/// @notice Deploy to deterministic addresses without an initcode factor. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/CREATE3.sol) +/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol) +library CREATE3 { + using Bytes32AddressLib for bytes32; + + //--------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //--------------------------------------------------------------------------------// + // 0x36 | 0x36 | CALLDATASIZE | size // + // 0x3d | 0x3d | RETURNDATASIZE | 0 size // + // 0x3d | 0x3d | RETURNDATASIZE | 0 0 size // + // 0x37 | 0x37 | CALLDATACOPY | // + // 0x36 | 0x36 | CALLDATASIZE | size // + // 0x3d | 0x3d | RETURNDATASIZE | 0 size // + // 0x34 | 0x34 | CALLVALUE | value 0 size // + // 0xf0 | 0xf0 | CREATE | newContract // + //--------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //--------------------------------------------------------------------------------// + // 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode // + // 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode // + // 0x52 | 0x52 | MSTORE | // + // 0x60 | 0x6008 | PUSH1 08 | 8 // + // 0x60 | 0x6018 | PUSH1 18 | 24 8 // + // 0xf3 | 0xf3 | RETURN | // + //--------------------------------------------------------------------------------// + bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3"; + + bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE); + + function deploy( + bytes32 salt, + bytes memory creationCode, + uint256 value + ) internal returns (address deployed) { + bytes memory proxyChildBytecode = PROXY_BYTECODE; + + address proxy; + assembly { + // Deploy a new contract with our pre-made bytecode via CREATE2. + // We start 32 bytes into the code to avoid copying the byte length. + proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt) + } + require(proxy != address(0), "DEPLOYMENT_FAILED"); + + deployed = getDeployed(salt); + (bool success, ) = proxy.call{value: value}(creationCode); + require(success && deployed.code.length != 0, "INITIALIZATION_FAILED"); + } + + function getDeployed(bytes32 salt) internal view returns (address) { + address proxy = keccak256( + abi.encodePacked( + // Prefix: + bytes1(0xFF), + // Creator: + address(this), + // Salt: + salt, + // Bytecode hash: + PROXY_BYTECODE_HASH + ) + ).fromLast20Bytes(); + + return + keccak256( + abi.encodePacked( + // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + hex"d6_94", + proxy, + hex"01" // Nonce of the proxy contract (1) + ) + ).fromLast20Bytes(); + } +} diff --git a/lib/solmate/src/utils/FixedPointMathLib.sol b/lib/solmate/src/utils/FixedPointMathLib.sol new file mode 100644 index 0000000..8fdaa03 --- /dev/null +++ b/lib/solmate/src/utils/FixedPointMathLib.sol @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Arithmetic library with operations for fixed-point numbers. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) +library FixedPointMathLib { + /*////////////////////////////////////////////////////////////// + SIMPLIFIED FIXED POINT OPERATIONS + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. + + function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. + } + + function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. + } + + function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. + } + + function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. + } + + function powWad(int256 x, int256 y) internal pure returns (int256) { + // Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y) + return expWad((lnWad(x) * y) / int256(WAD)); // Using ln(x) means x must be greater than 0. + } + + function expWad(int256 x) internal pure returns (int256 r) { + unchecked { + // When the result is < 0.5 we return zero. This happens when + // x <= floor(log(0.5e18) * 1e18) ~ -42e18 + if (x <= -42139678854452767551) return 0; + + // When the result is > (2**255 - 1) / 1e18 we can not represent it as an + // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135. + if (x >= 135305999368893231589) revert("EXP_OVERFLOW"); + + // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 + // for more intermediate precision and a binary basis. This base conversion + // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. + x = (x << 78) / 5**18; + + // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers + // of two such that exp(x) = exp(x') * 2**k, where k is an integer. + // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). + int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96; + x = x - k * 54916777467707473351141471128; + + // k is in the range [-61, 195]. + + // Evaluate using a (6, 7)-term rational approximation. + // p is made monic, we'll multiply by a scale factor later. + int256 y = x + 1346386616545796478920950773328; + y = ((y * x) >> 96) + 57155421227552351082224309758442; + int256 p = y + x - 94201549194550492254356042504812; + p = ((p * y) >> 96) + 28719021644029726153956944680412240; + p = p * x + (4385272521454847904659076985693276 << 96); + + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + int256 q = x - 2855989394907223263936484059900; + q = ((q * x) >> 96) + 50020603652535783019961831881945; + q = ((q * x) >> 96) - 533845033583426703283633433725380; + q = ((q * x) >> 96) + 3604857256930695427073651918091429; + q = ((q * x) >> 96) - 14423608567350463180887372962807573; + q = ((q * x) >> 96) + 26449188498355588339934803723976023; + + assembly { + // Div in assembly because solidity adds a zero check despite the unchecked. + // The q polynomial won't have zeros in the domain as all its roots are complex. + // No scaling is necessary because p is already 2**96 too large. + r := sdiv(p, q) + } + + // r should be in the range (0.09, 0.25) * 2**96. + + // We now need to multiply r by: + // * the scale factor s = ~6.031367120. + // * the 2**k factor from the range reduction. + // * the 1e18 / 2**96 factor for base conversion. + // We do this all at once, with an intermediate result in 2**213 + // basis, so the final right shift is always by a positive amount. + r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)); + } + } + + function lnWad(int256 x) internal pure returns (int256 r) { + unchecked { + require(x > 0, "UNDEFINED"); + + // We want to convert x from 10**18 fixed point to 2**96 fixed point. + // We do this by multiplying by 2**96 / 10**18. But since + // ln(x * C) = ln(x) + ln(C), we can simply do nothing here + // and add ln(2**96 / 10**18) at the end. + + // Reduce range of x to (1, 2) * 2**96 + // ln(2^k * x) = k * ln(2) + ln(x) + int256 k = int256(log2(uint256(x))) - 96; + x <<= uint256(159 - k); + x = int256(uint256(x) >> 159); + + // Evaluate using a (8, 8)-term rational approximation. + // p is made monic, we will multiply by a scale factor later. + int256 p = x + 3273285459638523848632254066296; + p = ((p * x) >> 96) + 24828157081833163892658089445524; + p = ((p * x) >> 96) + 43456485725739037958740375743393; + p = ((p * x) >> 96) - 11111509109440967052023855526967; + p = ((p * x) >> 96) - 45023709667254063763336534515857; + p = ((p * x) >> 96) - 14706773417378608786704636184526; + p = p * x - (795164235651350426258249787498 << 96); + + // We leave p in 2**192 basis so we don't need to scale it back up for the division. + // q is monic by convention. + int256 q = x + 5573035233440673466300451813936; + q = ((q * x) >> 96) + 71694874799317883764090561454958; + q = ((q * x) >> 96) + 283447036172924575727196451306956; + q = ((q * x) >> 96) + 401686690394027663651624208769553; + q = ((q * x) >> 96) + 204048457590392012362485061816622; + q = ((q * x) >> 96) + 31853899698501571402653359427138; + q = ((q * x) >> 96) + 909429971244387300277376558375; + assembly { + // Div in assembly because solidity adds a zero check despite the unchecked. + // The q polynomial is known not to have zeros in the domain. + // No scaling required because p is already 2**96 too large. + r := sdiv(p, q) + } + + // r is in the range (0, 0.125) * 2**96 + + // Finalization, we need to: + // * multiply by the scale factor s = 5.549… + // * add ln(2**96 / 10**18) + // * add k * ln(2) + // * multiply by 10**18 / 2**96 = 5**18 >> 78 + + // mul s * 5e18 * 2**96, base is now 5**18 * 2**192 + r *= 1677202110996718588342820967067443963516166; + // add ln(2) * k * 5e18 * 2**192 + r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k; + // add ln(2**96 / 10**18) * 5e18 * 2**192 + r += 600920179829731861736702779321621459595472258049074101567377883020018308; + // base conversion: mul 2**18 / 2**192 + r >>= 174; + } + } + + /*////////////////////////////////////////////////////////////// + LOW LEVEL FIXED POINT OPERATIONS + //////////////////////////////////////////////////////////////*/ + + function mulDivDown( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 z) { + assembly { + // Store x * y in z for now. + z := mul(x, y) + + // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) + if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { + revert(0, 0) + } + + // Divide z by the denominator. + z := div(z, denominator) + } + } + + function mulDivUp( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 z) { + assembly { + // Store x * y in z for now. + z := mul(x, y) + + // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) + if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { + revert(0, 0) + } + + // First, divide z - 1 by the denominator and add 1. + // We allow z - 1 to underflow if z is 0, because we multiply the + // end result by 0 if z is zero, ensuring we return 0 if z is zero. + z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) + } + } + + function rpow( + uint256 x, + uint256 n, + uint256 scalar + ) internal pure returns (uint256 z) { + assembly { + switch x + case 0 { + switch n + case 0 { + // 0 ** 0 = 1 + z := scalar + } + default { + // 0 ** n = 0 + z := 0 + } + } + default { + switch mod(n, 2) + case 0 { + // If n is even, store scalar in z for now. + z := scalar + } + default { + // If n is odd, store x in z for now. + z := x + } + + // Shifting right by 1 is like dividing by 2. + let half := shr(1, scalar) + + for { + // Shift n right by 1 before looping to halve it. + n := shr(1, n) + } n { + // Shift n right by 1 each iteration to halve it. + n := shr(1, n) + } { + // Revert immediately if x ** 2 would overflow. + // Equivalent to iszero(eq(div(xx, x), x)) here. + if shr(128, x) { + revert(0, 0) + } + + // Store x squared. + let xx := mul(x, x) + + // Round to the nearest number. + let xxRound := add(xx, half) + + // Revert if xx + half overflowed. + if lt(xxRound, xx) { + revert(0, 0) + } + + // Set x to scaled xxRound. + x := div(xxRound, scalar) + + // If n is even: + if mod(n, 2) { + // Compute z * x. + let zx := mul(z, x) + + // If z * x overflowed: + if iszero(eq(div(zx, x), z)) { + // Revert if x is non-zero. + if iszero(iszero(x)) { + revert(0, 0) + } + } + + // Round to the nearest number. + let zxRound := add(zx, half) + + // Revert if zx + half overflowed. + if lt(zxRound, zx) { + revert(0, 0) + } + + // Return properly scaled zxRound. + z := div(zxRound, scalar) + } + } + } + } + } + + /*////////////////////////////////////////////////////////////// + GENERAL NUMBER UTILITIES + //////////////////////////////////////////////////////////////*/ + + function sqrt(uint256 x) internal pure returns (uint256 z) { + assembly { + let y := x // We start y at x, which will help us make our initial estimate. + + z := 181 // The "correct" value is 1, but this saves a multiplication later. + + // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad + // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. + + // We check y >= 2^(k + 8) but shift right by k bits + // each branch to ensure that if x >= 256, then y >= 256. + if iszero(lt(y, 0x10000000000000000000000000000000000)) { + y := shr(128, y) + z := shl(64, z) + } + if iszero(lt(y, 0x1000000000000000000)) { + y := shr(64, y) + z := shl(32, z) + } + if iszero(lt(y, 0x10000000000)) { + y := shr(32, y) + z := shl(16, z) + } + if iszero(lt(y, 0x1000000)) { + y := shr(16, y) + z := shl(8, z) + } + + // Goal was to get z*z*y within a small factor of x. More iterations could + // get y in a tighter range. Currently, we will have y in [256, 256*2^16). + // We ensured y >= 256 so that the relative difference between y and y+1 is small. + // That's not possible if x < 256 but we can just verify those cases exhaustively. + + // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. + // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. + // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. + + // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range + // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. + + // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate + // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. + + // There is no overflow risk here since y < 2^136 after the first branch above. + z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. + + // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + + // If x+1 is a perfect square, the Babylonian method cycles between + // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. + // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division + // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. + // If you don't care whether the floor or ceil square root is returned, you can remove this statement. + z := sub(z, lt(div(x, z), z)) + } + } + + function log2(uint256 x) internal pure returns (uint256 r) { + require(x > 0, "UNDEFINED"); + + assembly { + r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) + r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) + r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) + r := or(r, shl(4, lt(0xffff, shr(r, x)))) + r := or(r, shl(3, lt(0xff, shr(r, x)))) + r := or(r, shl(2, lt(0xf, shr(r, x)))) + r := or(r, shl(1, lt(0x3, shr(r, x)))) + r := or(r, lt(0x1, shr(r, x))) + } + } + + function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + // z will equal 0 if y is 0, unlike in Solidity where it will revert. + z := mod(x, y) + } + } + + function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + // z will equal 0 if y is 0, unlike in Solidity where it will revert. + z := div(x, y) + } + } + + /// @dev Will return 0 instead of reverting if y is zero. + function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + // Add 1 to x * y if x % y > 0. + z := add(gt(mod(x, y), 0), div(x, y)) + } + } +} diff --git a/lib/solmate/src/utils/LibBitmap.sol b/lib/solmate/src/utils/LibBitmap.sol new file mode 100644 index 0000000..0895319 --- /dev/null +++ b/lib/solmate/src/utils/LibBitmap.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Efficient bitmap library for mapping integers to single bit booleans. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibBitmap.sol) +library LibBitmap { + struct Bitmap { + mapping(uint256 => uint256) map; + } + + function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) { + uint256 value = bitmap.map[index >> 8] & (1 << (index & 0xff)); + + assembly { + isSet := value // Assign isSet to whether the value is non zero. + } + } + + function set(Bitmap storage bitmap, uint256 index) internal { + bitmap.map[index >> 8] |= (1 << (index & 0xff)); + } + + function unset(Bitmap storage bitmap, uint256 index) internal { + bitmap.map[index >> 8] &= ~(1 << (index & 0xff)); + } + + function setTo( + Bitmap storage bitmap, + uint256 index, + bool shouldSet + ) internal { + uint256 value = bitmap.map[index >> 8]; + + assembly { + // The following sets the bit at `shift` without branching. + let shift := and(index, 0xff) + // Isolate the bit at `shift`. + let x := and(shr(shift, value), 1) + // Xor it with `shouldSet`. Results in 1 if both are different, else 0. + x := xor(x, shouldSet) + // Shifts the bit back. Then, xor with value. + // Only the bit at `shift` will be flipped if they differ. + // Every other bit will stay the same, as they are xor'ed with zeroes. + value := xor(value, shl(shift, x)) + } + bitmap.map[index >> 8] = value; + } +} diff --git a/lib/solmate/src/utils/LibERB.sol b/lib/solmate/src/utils/LibERB.sol new file mode 100644 index 0000000..2cb41a2 --- /dev/null +++ b/lib/solmate/src/utils/LibERB.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {FixedPointMathLib} from "./FixedPointMathLib.sol"; + +// TODO: Should we return the updateNumber from the read funcs? + +// TODO: Does a fixed size array save gas vs a dynamic or mapping? + +// TODO: Prove conjectures about ERB properties with Forge invariants. + +// TODO: Could make this more efficient if we removed availableSlots +// and only allowed grow to be called when index == populatedSlots - 1. + +// TODO: Technically don't need an ERBValue struct? Unless people find +// knowing the updateNumber helpful? It also allows touching the array +// without messing up value in grow tho. If we removed I'd like to add +// the "can't read uninitialized" slots rule back to the read functions. +// I think the best thing to do would be to make it a generic param, then +// if users find it useful they can attach update number, or timestamp, etc. + +/*////////////////////////////////////////////////////////////// + ERB +//////////////////////////////////////////////////////////////*/ + +/// @notice Low-level library for interacting with an expandable ring buffer. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibERB.sol) +library LibERB { + struct ERBValue { + uint224 value; + uint32 updateNumber; + } + + // Expected invariants: + // - availableSlots >= 1 + // - totalUpdates >= populatedSlots + // - totalUpdates < type(uint32).max + // - availableSlots >= populatedSlots + + function grow( + ERBValue[65535] storage self, + uint16 growBy, + uint16 availableSlots + ) internal returns (uint16 newTotalAvailableSlots) { + // This will overflow if we're trying to grow by too much. + newTotalAvailableSlots = availableSlots + growBy; + + unchecked { + for (uint256 i = availableSlots; i < newTotalAvailableSlots; i++) + // We already implicitly assume we'll never + // reach type(uint32).max many total updates. + self[i].updateNumber = type(uint32).max; + } + } + + function write( + ERBValue[65535] storage self, + uint224 value, + uint32 totalUpdates, + uint16 populatedSlots, + uint16 availableSlots // Note: This MUST be at least 1. + ) internal returns (uint32 newTotalUpdates, uint16 newPopulatedSlots) { + unchecked { + // TODO: hmm ok we also need to make sure we dont exceed available slots + // TODO: wait why do we even need to do this??? + // TODO: also wait confused about the root cause, why does poulpated slots matter again + newPopulatedSlots = populatedSlots == 0 || + (totalUpdates % populatedSlots == (populatedSlots - 1) && populatedSlots < availableSlots) + ? populatedSlots + 1 + : populatedSlots; + + newTotalUpdates = totalUpdates + 1; // This will silently overflow if we reach type(uint32).max updates. + + self[totalUpdates % newPopulatedSlots] = ERBValue({value: value, updateNumber: newTotalUpdates}); + } + } + + function read( + ERBValue[65535] storage self, + uint32 totalUpdates, + uint16 populatedSlots + ) internal view returns (ERBValue memory) { + // We use unsafeMod so that we use index 0 when populatedSlots is 0. + return self[FixedPointMathLib.unsafeMod(totalUpdates, populatedSlots)]; + } + + function readOffset( + ERBValue[65535] storage self, + uint32 offset, + uint32 totalUpdates, + uint16 populatedSlots + ) internal view returns (ERBValue memory) { + unchecked { + // We can't read back further than our # of populated slots will allow. + require(offset <= populatedSlots, "OUT_OF_BOUNDS"); + + // We use unsafeMod so that we use index 0 when populatedSlots is 0. + // We assume the invariant totalUpdates >= populatedSlots is maintained. + return self[FixedPointMathLib.unsafeMod(totalUpdates - offset, populatedSlots)]; + } + } + + function readUpdateNumber( + ERBValue[65535] storage self, + uint32 updateNumber, + uint32 totalUpdates, + uint16 populatedSlots + ) internal view returns (ERBValue memory) { + // We can't read back further than our # of populated slots will allow. + // This will safely revert due to underflow if updateNumber > totalUpdates. + require(totalUpdates - updateNumber <= populatedSlots, "OUT_OF_BOUNDS"); + + // Return early if we just wan't the current value. We use + // unsafeMod so that we use index 0 when populatedSlots is 0. + return self[FixedPointMathLib.unsafeMod(updateNumber, populatedSlots)]; + } +} + +/*////////////////////////////////////////////////////////////// + BOXED ERB +//////////////////////////////////////////////////////////////*/ + +/// @notice High-level library for interacting with a self-managing encapsulated ERB. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibERB.sol) +library LibBoxedERB { + struct BoxedERB { + uint32 totalUpdates; + uint16 populatedSlots; + uint16 availableSlots; + LibERB.ERBValue[65535] erb; + } + + using LibERB for LibERB.ERBValue[65535]; + + // This should be called once and only + // once over the lifetime of the contract, + // before any calls to write are attempted. + function init(BoxedERB storage self) internal { + self.availableSlots = 1; + + // We already implicitly assume we'll never + // reach type(uint32).max many total updates. + self.erb[0].updateNumber = type(uint32).max; + } + + function grow(BoxedERB storage self, uint16 growBy) internal { + self.availableSlots = self.erb.grow(growBy, self.availableSlots); + } + + // This should only be called if init has been called before. + function write(BoxedERB storage self, uint224 value) internal { + (self.totalUpdates, self.populatedSlots) = self.erb.write( + value, + self.totalUpdates, + self.populatedSlots, + self.availableSlots + ); + } + + function read(BoxedERB storage self) internal view returns (LibERB.ERBValue memory) { + return self.erb.read(self.totalUpdates, self.populatedSlots); + } + + function readOffset(BoxedERB storage self, uint32 offset) internal view returns (LibERB.ERBValue memory) { + return self.erb.readOffset(offset, self.totalUpdates, self.populatedSlots); + } + + function readUpdateNumber(BoxedERB storage self, uint32 updateNumber) + internal + view + returns (LibERB.ERBValue memory) + { + return self.erb.readUpdateNumber(updateNumber, self.totalUpdates, self.populatedSlots); + } +} diff --git a/lib/solmate/src/utils/LibRLP.sol b/lib/solmate/src/utils/LibRLP.sol new file mode 100644 index 0000000..6435f15 --- /dev/null +++ b/lib/solmate/src/utils/LibRLP.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {Bytes32AddressLib} from "./Bytes32AddressLib.sol"; + +/// @notice Library for computing contract addresses from their deployer and nonce. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol) +library LibRLP { + using Bytes32AddressLib for bytes32; + + // prettier-ignore + function computeAddress(address deployer, uint256 nonce) internal pure returns (address) { + // The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0. + // A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it. + if (nonce == 0x00) return keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))).fromLast20Bytes(); + if (nonce <= 0x7f) return keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce))).fromLast20Bytes(); + + // Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length. + if (nonce <= type(uint8).max) return keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce))).fromLast20Bytes(); + if (nonce <= type(uint16).max) return keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce))).fromLast20Bytes(); + if (nonce <= type(uint24).max) return keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce))).fromLast20Bytes(); + + // More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp + // 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + // 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex) + // We assume nobody can have a nonce large enough to require more than 32 bytes. + return keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))).fromLast20Bytes(); + } +} diff --git a/lib/solmate/src/utils/LibString.sol b/lib/solmate/src/utils/LibString.sol new file mode 100644 index 0000000..04c0781 --- /dev/null +++ b/lib/solmate/src/utils/LibString.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Efficient library for creating string representations of integers. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibString.sol) +library LibString { + function toString(uint256 n) internal pure returns (string memory str) { + if (n == 0) return "0"; // Otherwise it'd output an empty string for 0. + + assembly { + let k := 78 // Start with the max length a uint256 string could be. + + // We'll store our string at the first chunk of free memory. + str := mload(0x40) + + // The length of our string will start off at the max of 78. + mstore(str, k) + + // Update the free memory pointer to prevent overriding our string. + // Add 128 to the str pointer instead of 78 because we want to maintain + // the Solidity convention of keeping the free memory pointer word aligned. + mstore(0x40, add(str, 128)) + + // We'll populate the string from right to left. + // prettier-ignore + for {} n {} { + // The ASCII digit offset for '0' is 48. + let char := add(48, mod(n, 10)) + + // Write the current character into str. + mstore(add(str, k), char) + + k := sub(k, 1) + n := div(n, 10) + } + + // Shift the pointer to the start of the string. + str := add(str, k) + + // Set the length of the string to the correct value. + mstore(str, sub(78, k)) + } + } +} diff --git a/lib/solmate/src/utils/MerkleProofLib.sol b/lib/solmate/src/utils/MerkleProofLib.sol new file mode 100644 index 0000000..ee3299e --- /dev/null +++ b/lib/solmate/src/utils/MerkleProofLib.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Gas optimized merkle proof verification library. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/MerkleProofLib.sol) +library MerkleProofLib { + function verify( + bytes32[] calldata proof, + bytes32 root, + bytes32 leaf + ) internal pure returns (bool isValid) { + assembly { + let computedHash := leaf // The hash starts as the leaf hash. + + // Initialize data to the offset of the proof in the calldata. + let data := proof.offset + + // Iterate over proof elements to compute root hash. + for { + // Left shifting by 5 is like multiplying by 32. + let end := add(data, shl(5, proof.length)) + } lt(data, end) { + data := add(data, 32) // Shift 1 word per cycle. + } { + // Load the current proof element. + let loadedData := calldataload(data) + + // Slot where computedHash should be put in scratch space. + // If computedHash > loadedData: slot 32, otherwise: slot 0. + let computedHashSlot := shl(5, gt(computedHash, loadedData)) + + // Store elements to hash contiguously in scratch space. + // The xor puts loadedData in whichever slot computedHash is + // not occupying, so 0 if computedHashSlot is 32, 32 otherwise. + mstore(computedHashSlot, computedHash) + mstore(xor(computedHashSlot, 32), loadedData) + + computedHash := keccak256(0, 64) // Hash both slots of scratch space. + } + + isValid := eq(computedHash, root) // The proof is valid if the roots match. + } + } +} diff --git a/lib/solmate/src/utils/ReentrancyGuard.sol b/lib/solmate/src/utils/ReentrancyGuard.sol new file mode 100644 index 0000000..332e468 --- /dev/null +++ b/lib/solmate/src/utils/ReentrancyGuard.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Gas optimized reentrancy protection for smart contracts. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) +abstract contract ReentrancyGuard { + uint256 private locked = 1; + + modifier nonReentrant() virtual { + require(locked == 1, "REENTRANCY"); + + locked = 2; + + _; + + locked = 1; + } +} diff --git a/lib/solmate/src/utils/SSTORE2.sol b/lib/solmate/src/utils/SSTORE2.sol new file mode 100644 index 0000000..129b9da --- /dev/null +++ b/lib/solmate/src/utils/SSTORE2.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Read and write to persistent storage at a fraction of the cost. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SSTORE2.sol) +library SSTORE2 { + uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called. + + /*////////////////////////////////////////////////////////////// + WRITE LOGIC + //////////////////////////////////////////////////////////////*/ + + function write(bytes memory data) internal returns (address pointer) { + // Prefix the bytecode with a STOP opcode to ensure it cannot be called. + bytes memory runtimeCode = abi.encodePacked(hex"00", data); + + bytes memory creationCode = abi.encodePacked( + //---------------------------------------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //---------------------------------------------------------------------------------------------------------------// + // 0x60 | 0x600B | PUSH1 11 | codeOffset // + // 0x59 | 0x59 | MSIZE | 0 codeOffset // + // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // + // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // + // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // + // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // + // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // + // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // + // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // + // 0xf3 | 0xf3 | RETURN | // + //---------------------------------------------------------------------------------------------------------------// + hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. + runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. + ); + + assembly { + // Deploy a new contract with the generated creation code. + // We start 32 bytes into the code to avoid copying the byte length. + pointer := create(0, add(creationCode, 32), mload(creationCode)) + } + + require(pointer != address(0), "DEPLOYMENT_FAILED"); + } + + /*////////////////////////////////////////////////////////////// + READ LOGIC + //////////////////////////////////////////////////////////////*/ + + function read(address pointer) internal view returns (bytes memory) { + return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET); + } + + function read(address pointer, uint256 start) internal view returns (bytes memory) { + start += DATA_OFFSET; + + return readBytecode(pointer, start, pointer.code.length - start); + } + + function read( + address pointer, + uint256 start, + uint256 end + ) internal view returns (bytes memory) { + start += DATA_OFFSET; + end += DATA_OFFSET; + + require(pointer.code.length >= end, "OUT_OF_BOUNDS"); + + return readBytecode(pointer, start, end - start); + } + + /*////////////////////////////////////////////////////////////// + INTERNAL HELPER LOGIC + //////////////////////////////////////////////////////////////*/ + + function readBytecode( + address pointer, + uint256 start, + uint256 size + ) private view returns (bytes memory data) { + assembly { + // Get a pointer to some free memory. + data := mload(0x40) + + // Update the free memory pointer to prevent overriding our data. + // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)). + // Adding 31 to size and running the result through the logic above ensures + // the memory pointer remains word-aligned, following the Solidity convention. + mstore(0x40, add(data, and(add(add(size, 32), 31), not(31)))) + + // Store the size of the data in the first 32 byte chunk of free memory. + mstore(data, size) + + // Copy the code into memory right after the 32 bytes we used to store the size. + extcodecopy(pointer, add(data, 32), start, size) + } + } +} diff --git a/lib/solmate/src/utils/SafeCastLib.sol b/lib/solmate/src/utils/SafeCastLib.sol new file mode 100644 index 0000000..75c0e72 --- /dev/null +++ b/lib/solmate/src/utils/SafeCastLib.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +/// @notice Safe unsigned integer casting library that reverts on overflow. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeCastLib.sol) +library SafeCastLib { + function safeCastTo248(uint256 x) internal pure returns (uint248 y) { + require(x < 1 << 248); + + y = uint248(x); + } + + function safeCastTo224(uint256 x) internal pure returns (uint224 y) { + require(x < 1 << 224); + + y = uint224(x); + } + + function safeCastTo192(uint256 x) internal pure returns (uint192 y) { + require(x < 1 << 192); + + y = uint192(x); + } + + function safeCastTo160(uint256 x) internal pure returns (uint160 y) { + require(x < 1 << 160); + + y = uint160(x); + } + + function safeCastTo128(uint256 x) internal pure returns (uint128 y) { + require(x < 1 << 128); + + y = uint128(x); + } + + function safeCastTo96(uint256 x) internal pure returns (uint96 y) { + require(x < 1 << 96); + + y = uint96(x); + } + + function safeCastTo64(uint256 x) internal pure returns (uint64 y) { + require(x < 1 << 64); + + y = uint64(x); + } + + function safeCastTo32(uint256 x) internal pure returns (uint32 y) { + require(x < 1 << 32); + + y = uint32(x); + } + + function safeCastTo8(uint256 x) internal pure returns (uint8 y) { + require(x < 1 << 8); + + y = uint8(x); + } +} diff --git a/lib/solmate/src/utils/SafeTransferLib.sol b/lib/solmate/src/utils/SafeTransferLib.sol new file mode 100644 index 0000000..849bea2 --- /dev/null +++ b/lib/solmate/src/utils/SafeTransferLib.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import {ERC20} from "../tokens/ERC20.sol"; + +/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) +/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller. +library SafeTransferLib { + /*////////////////////////////////////////////////////////////// + ETH OPERATIONS + //////////////////////////////////////////////////////////////*/ + + function safeTransferETH(address to, uint256 amount) internal { + bool success; + + assembly { + // Transfer the ETH and store if it succeeded or not. + success := call(gas(), to, amount, 0, 0, 0, 0) + } + + require(success, "ETH_TRANSFER_FAILED"); + } + + /*////////////////////////////////////////////////////////////// + ERC20 OPERATIONS + //////////////////////////////////////////////////////////////*/ + + function safeTransferFrom( + ERC20 token, + address from, + address to, + uint256 amount + ) internal { + bool success; + + assembly { + // We'll write our calldata to this slot below, but restore it later. + let memPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) + mstore(4, from) // Append the "from" argument. + mstore(36, to) // Append the "to" argument. + mstore(68, amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 100 because that's the total length of our calldata (4 + 32 * 3) + // Counterintuitively, this call() must be positioned after the or() in the + // surrounding and() because and() evaluates its arguments from right to left. + call(gas(), token, 0, 0, 100, 0, 32) + ) + + mstore(0x60, 0) // Restore the zero slot to zero. + mstore(0x40, memPointer) // Restore the memPointer. + } + + require(success, "TRANSFER_FROM_FAILED"); + } + + function safeTransfer( + ERC20 token, + address to, + uint256 amount + ) internal { + bool success; + + assembly { + // We'll write our calldata to this slot below, but restore it later. + let memPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(0, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) + mstore(4, to) // Append the "to" argument. + mstore(36, amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because that's the total length of our calldata (4 + 32 * 2) + // Counterintuitively, this call() must be positioned after the or() in the + // surrounding and() because and() evaluates its arguments from right to left. + call(gas(), token, 0, 0, 68, 0, 32) + ) + + mstore(0x60, 0) // Restore the zero slot to zero. + mstore(0x40, memPointer) // Restore the memPointer. + } + + require(success, "TRANSFER_FAILED"); + } + + function safeApprove( + ERC20 token, + address to, + uint256 amount + ) internal { + bool success; + + assembly { + // We'll write our calldata to this slot below, but restore it later. + let memPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(0, 0x095ea7b300000000000000000000000000000000000000000000000000000000) + mstore(4, to) // Append the "to" argument. + mstore(36, amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because that's the total length of our calldata (4 + 32 * 2) + // Counterintuitively, this call() must be positioned after the or() in the + // surrounding and() because and() evaluates its arguments from right to left. + call(gas(), token, 0, 0, 68, 0, 32) + ) + + mstore(0x60, 0) // Restore the zero slot to zero. + mstore(0x40, memPointer) // Restore the memPointer. + } + + require(success, "APPROVE_FAILED"); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f94dd6b --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "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/remappings.txt b/remappings.txt new file mode 100644 index 0000000..c8dffc5 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,5 @@ +@solmate=lib/solmate/src/ +@std=lib/forge-std/src/ +@clones=lib/clones-with-immutable-args/src/ +@chainlink/=lib/chainlink-brownie-contracts/ +forge-std/=lib/forge-std/src/ \ No newline at end of file diff --git a/slither.config.json b/slither.config.json new file mode 100644 index 0000000..66dc87f --- /dev/null +++ b/slither.config.json @@ -0,0 +1,9 @@ +{ + "filter_paths": "lib", + "solc_remaps": [ + "ds-test/=lib/ds-test/src/", + "forge-std/=lib/forge-std/src/", + "@chainlink/=lib/chainlink-brownie-contracts/" + ] + } + \ No newline at end of file diff --git a/src/BFKLoanMarket.sol b/src/BFKLoanMarket.sol new file mode 100644 index 0000000..ec6db2b --- /dev/null +++ b/src/BFKLoanMarket.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import "@solmate/auth/Owned.sol"; +import "./interfaces/IPriceOracle.sol"; +import "@solmate/tokens/ERC20.sol"; + +/** + * STEAL THEIR BLEMFLARCK! DEATH TO EIGER! + */ +contract BFKLoanMarket is Owned(msg.sender) { + IPriceOracle public immutable oracle; + ERC20 public immutable blemflarck; + ERC20 public immutable jungfrau; + + uint256 public constant COLLATERALIZATION_PERCENT = 150; + uint256 public constant LIQUIDATION_THRESHOLD = 20; + + Round[] public priceOverrides; + + struct Loan { + uint256 bfkAmount; + uint256 openingPrice; + } + + struct Round { + int256 price; + uint256 timestamp; + } + + mapping(address => Loan) public loans; + + constructor( + address bfk, + address jfr, + address priceOracleAddress + ) { + blemflarck = ERC20(bfk); + jungfrau = ERC20(jfr); + // 0x7d7356bF6Ee5CDeC22B216581E48eCC700D0497A EUR/USD + oracle = IPriceOracle(priceOracleAddress); + } + + function latestAnswer() internal view returns (uint256) { + (, int256 answer, , uint256 timestamp, ) = oracle.latestRoundData(); + uint256 returnedAnswer = uint256(answer); + if ( + priceOverrides[priceOverrides.length - 1].timestamp > + timestamp - 120 + ) { + returnedAnswer = uint256( + priceOverrides[priceOverrides.length - 1].price + ); + } + return returnedAnswer; + } + + function _loan(address loanTaker, uint256 collateral) internal { + uint256 latestPrice = latestAnswer(); + loans[loanTaker] = Loan({ + bfkAmount: collateral, + openingPrice: latestPrice + }); + + blemflarck.transferFrom(loanTaker, address(this), collateral); + + uint256 loanAmount = ((collateral * latestPrice) / + 10**8 / + COLLATERALIZATION_PERCENT) * 100; + jungfrau.transfer(loanTaker, loanAmount); + } + + function _liquidate( + address loanTaker, + address liquidator, + uint256 repayment, + uint256 price + ) internal { + require( + (price * 100) / loans[loanTaker].openingPrice < + 100 - LIQUIDATION_THRESHOLD, + "Loan still valid" + ); + loans[loanTaker] = Loan({bfkAmount: 0, openingPrice: 0}); + jungfrau.transferFrom(liquidator, address(this), repayment); + + uint256 liquidatorReward = (repayment / price) * 10**8; + blemflarck.transfer(liquidator, liquidatorReward); + } + + function loan(uint256 collateral) public { + _loan(msg.sender, collateral); + } + + function updatePrice(int256 price) public onlyOwner { + priceOverrides.push(Round({price: price, timestamp: block.timestamp})); + } +} diff --git a/src/Blemflarck.sol b/src/Blemflarck.sol new file mode 100644 index 0000000..6ce2cc5 --- /dev/null +++ b/src/Blemflarck.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import "@solmate/tokens/ERC20.sol"; +import "@solmate/auth/Owned.sol"; + +contract Blemflarck is ERC20("Blemflarck", "BFK", 18), Owned(msg.sender) { + function mint(address to, uint256 amount) external onlyOwner { + _mint(to, amount); + } +} diff --git a/src/Jungfrau.sol b/src/Jungfrau.sol new file mode 100644 index 0000000..8707df4 --- /dev/null +++ b/src/Jungfrau.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import "@solmate/tokens/ERC20.sol"; +import "@solmate/auth/Owned.sol"; + +/** + * LONG LIVE THE PIRATES! DEATH TO EIGER! + */ +contract Jungfrau is ERC20("Jungfrau", "JFR", 18), Owned(msg.sender) { + function mint(address to, uint256 amount) external onlyOwner { + _mint(to, amount); + } +} diff --git a/src/interfaces/IPriceOracle.sol b/src/interfaces/IPriceOracle.sol new file mode 100644 index 0000000..b9f7588 --- /dev/null +++ b/src/interfaces/IPriceOracle.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.10; + +interface IPriceOracle { + /// @notice Gets the decimals used in `latestAnswer()`. + function decimals() external view returns (uint8); + + /// @notice Gets the price change data for exact roundId (i.e. an identifier for single historical price change in the Oracle) + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + /// @notice Gets the latest price change data. + /// @dev Intentionally the same name and return values as the Chainlink aggregator interface (https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.4/interfaces/AggregatorV3Interface.sol). + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/src/test/BFKLoanMarket.t.sol b/src/test/BFKLoanMarket.t.sol new file mode 100644 index 0000000..e69de29 diff --git a/src/test/Blemflarck.t.sol b/src/test/Blemflarck.t.sol new file mode 100644 index 0000000..9a09d5c --- /dev/null +++ b/src/test/Blemflarck.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../Blemflarck.sol"; +import "forge-std/Test.sol"; +import "./mocks/MockOracle.sol"; + +contract BlemflarckTest is Test { + Blemflarck public blemflarck; + + uint256 public constant AMOUNT = 42 * 10**18; + + function setUp() public { + blemflarck = new Blemflarck(); + } + + function testOwnerCanMint() public { + blemflarck.mint(msg.sender, AMOUNT); + uint256 balance = blemflarck.balanceOf(msg.sender); + assertEq(balance, AMOUNT); + } +} diff --git a/src/test/Jungfrau.t.sol b/src/test/Jungfrau.t.sol new file mode 100644 index 0000000..1364254 --- /dev/null +++ b/src/test/Jungfrau.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../Jungfrau.sol"; +import "forge-std/Test.sol"; +import "./mocks/MockOracle.sol"; + +contract JungfrauTest is Test { + Jungfrau public jungfrau; + + uint256 public constant AMOUNT = 1 * 10**18; + + function setUp() public { + jungfrau = new Jungfrau(); + } + + function testOwnerCanMint() public { + jungfrau.mint(msg.sender, AMOUNT); + uint256 balance = jungfrau.balanceOf(msg.sender); + assertEq(balance, AMOUNT); + } +} diff --git a/src/test/mocks/LinkToken.sol b/src/test/mocks/LinkToken.sol new file mode 100644 index 0000000..3fc0a08 --- /dev/null +++ b/src/test/mocks/LinkToken.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT + +// @dev This contract has been adapted to fit with dappTools +pragma solidity ^0.8.0; + +import "@solmate/tokens/ERC20.sol"; + +interface ERC677Receiver { + function onTokenTransfer( + address _sender, + uint256 _value, + bytes memory _data + ) external; +} + +contract LinkToken is ERC20 { + uint256 constant INITIAL_SUPPLY = 1000000000000000000000000; + uint8 constant DECIMALS = 18; + + constructor() ERC20("LinkToken", "LINK", DECIMALS) { + _mint(msg.sender, INITIAL_SUPPLY); + } + + 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. + * @param _to The address to transfer to. + * @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) { + super.transfer(_to, _value); + // emit Transfer(msg.sender, _to, _value, _data); + emit Transfer(msg.sender, _to, _value, _data); + if (isContract(_to)) { + contractFallback(_to, _value, _data); + } + return true; + } + + // PRIVATE + + function contractFallback( + address _to, + uint256 _value, + bytes memory _data + ) private { + ERC677Receiver receiver = ERC677Receiver(_to); + receiver.onTokenTransfer(msg.sender, _value, _data); + } + + function isContract(address _addr) private view returns (bool hasCode) { + uint256 length; + assembly { + length := extcodesize(_addr) + } + return length > 0; + } +} diff --git a/src/test/mocks/MockPriceOracle.sol b/src/test/mocks/MockPriceOracle.sol new file mode 100644 index 0000000..98e7173 --- /dev/null +++ b/src/test/mocks/MockPriceOracle.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.10; + +import "../interfaces/IPriceOracle.sol"; + +contract MockPriceOracle is IPriceOracle { + struct MockRound { + int256 price; + uint256 timestamp; + } + + MockRound[] private rounds; + uint256 private constant PHASE_OFFSET = 64; + uint256 private constant PHASE_SIZE = 16; + uint16 private phaseId; + + function decimals() external pure returns (uint8) { + return 8; + } + + function latestPrice() external view returns (int256) { + MockRound memory round = rounds[rounds.length - 1]; + return round.price; + } + + function setPrice(int256 newPrice) external { + rounds.push(MockRound({price: newPrice, timestamp: block.timestamp})); + } + + function setPhaseId(uint16 pid) external { + phaseId = pid; + } + + function decodeId(uint256 roundId) + internal + pure + returns (uint16 phaseId, uint64 realRoundId) + { + phaseId = uint16(roundId >> PHASE_OFFSET); + realRoundId = uint64(roundId); + } + + function getRoundData(uint80 roundId) + external + view + returns ( + uint80, + int256, + uint256, + uint256, + uint80 + ) + { + (uint16 phaseId, uint64 rid) = decodeId(roundId); + MockRound memory round = rounds[rid - 1]; + return ( + roundId, + round.price, + round.timestamp, + round.timestamp, + roundId + ); + } + + function addPhase(uint16 phase, uint64 originalId) + internal + pure + returns (uint80) + { + return uint80((uint256(phase) << PHASE_OFFSET) | originalId); + } + + function latestRoundData() + external + view + returns ( + uint80, + int256, + uint256, + uint256, + uint80 + ) + { + uint80 roundId = addPhase(phaseId, uint64(rounds.length)); + MockRound memory round = rounds[rounds.length - 1]; + return ( + roundId, + round.price, + round.timestamp, + round.timestamp, + roundId + ); + } +} diff --git a/src/test/mocks/MockV3Aggregator.sol b/src/test/mocks/MockV3Aggregator.sol new file mode 100644 index 0000000..411c9b9 --- /dev/null +++ b/src/test/mocks/MockV3Aggregator.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title MockV3Aggregator + * @notice Based on the FluxAggregator contract + * @notice Use this contract when you need to test + * other contract's ability to read data from an + * aggregator contract, but how the aggregator got + * its answer is unimportant + */ +contract MockV3Aggregator { + uint256 public constant version = 0; + + uint8 public decimals; + int256 public latestAnswer; + uint256 public latestTimestamp; + uint256 public latestRound; + + mapping(uint256 => int256) public getAnswer; + mapping(uint256 => uint256) public getTimestamp; + mapping(uint256 => uint256) private getStartedAt; + + constructor(uint8 _decimals, int256 _initialAnswer) { + decimals = _decimals; + updateAnswer(_initialAnswer); + } + + function updateAnswer(int256 _answer) public { + latestAnswer = _answer; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = block.timestamp; + getStartedAt[latestRound] = block.timestamp; + } + + function updateRoundData( + uint80 _roundId, + int256 _answer, + uint256 _timestamp, + uint256 _startedAt + ) public { + latestRound = _roundId; + latestAnswer = _answer; + latestTimestamp = _timestamp; + getAnswer[latestRound] = _answer; + getTimestamp[latestRound] = _timestamp; + getStartedAt[latestRound] = _startedAt; + } + + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + _roundId, + getAnswer[_roundId], + getStartedAt[_roundId], + getTimestamp[_roundId], + _roundId + ); + } + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return ( + uint80(latestRound), + getAnswer[latestRound], + getStartedAt[latestRound], + getTimestamp[latestRound], + uint80(latestRound) + ); + } + + function description() external pure returns (string memory) { + return "v0.6/tests/MockV3Aggregator.sol"; + } +} diff --git a/src/test/mocks/MockVRFCoordinatorV2.sol b/src/test/mocks/MockVRFCoordinatorV2.sol new file mode 100644 index 0000000..6dc7e83 --- /dev/null +++ b/src/test/mocks/MockVRFCoordinatorV2.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol"; + +contract MockVRFCoordinatorV2 is VRFCoordinatorV2Mock { + uint96 constant MOCK_BASE_FEE = 100000000000000000; + uint96 constant MOCK_GAS_PRICE_LINK = 1e9; + + constructor() VRFCoordinatorV2Mock(MOCK_BASE_FEE, MOCK_GAS_PRICE_LINK) {} +} diff --git a/src/test/utils/Cheats.sol b/src/test/utils/Cheats.sol new file mode 100644 index 0000000..1ffdaee --- /dev/null +++ b/src/test/utils/Cheats.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +abstract contract Cheats { + // sets the block timestamp to x + function warp(uint256 x) public virtual; + + // sets the block number to x + 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 ffi(string[] calldata) external virtual returns (bytes memory); + + function expectEmit( + bool, + bool, + bool, + bool + ) external virtual; + + function expectRevert(bytes calldata msg) external virtual; +}