diff --git a/scripts/.env.example b/scripts/.env.example index f502aa8..26f6995 100644 --- a/scripts/.env.example +++ b/scripts/.env.example @@ -2,3 +2,6 @@ TENDERLY_USER= TENDERLY_PROJECT= TENDERLY_ACCESS_KEY= +COINBASE_API_KEY= +COINBASE_API_SECRET= +COINBASE_API_PASSPHRASE= \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md index b741dc2..f9327c3 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -36,3 +36,43 @@ The script will create Tenderly forks that have Note starting with `Generated: 0 identify the forks that it had created and delete them when creating the same type of simulation. If you are sharing Tenderly forks with other people, it is better to remove the `Generated: 0x...` Note from the fork through Tenderly UI. + + +# Coinbase API Price Scripts + +The following describes the Coinbase API Price Scripts to fetch cryptocurrency prices from the Coinbase API and logs related messages and signatures. + +## Prerequisites + +Before running the scripts you need to create an .env file in the scripts directory of the project and add the following environment variables: + +``` + COINBASE_API_KEY=your_api_key + COINBASE_API_SECRET=your_api_secret + COINBASE_API_PASSPHRASE=your_api_passphrase +``` + +## Installation + +To set up the project, you need to install the necessary dependencies. You can do this by running the following command in your terminal: + +``` + yarn install +``` + +## Running the Scripts + +Once the installation is complete, you can start the scripts with the following command: + +Fetch data from the Coinbase API and save it to a file: +``` + node ./src/fetchData.js +``` +Read data from the file and send it to stdout: +``` + node ./src/readData.js +``` + +## Integrating Signatures and Messages + +test/unit/CoinbaseOracle.sol uses the out `fetchData.js` and `readData.js` scripts to fetch data from the Coinbase API and push it the CoinbaseOracle smart contract. Make sure the .env file is set up correctly before running the forge test. \ No newline at end of file diff --git a/scripts/package.json b/scripts/package.json index 8b8e457..77e06ac 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -14,14 +14,16 @@ "clean": "rm -rf contract-types && rm -rf dist && rm -rf node_modules" }, "devDependencies": { + "@redstone-finance/protocol": "^0.5.1", + "@redstone-finance/sdk": "^0.5.1", "@typechain/ethers-v5": "^11.1.2", "@types/node": "^20.8.6", - "typechain": "^8.3.2", - "typescript": "^5.2.2", "axios": "^1.5.1", - "dotenv": "^16.3.1", + "dotenv": "^16.4.5", "ethers": "^5.7.2", - "@redstone-finance/protocol": "^0.5.1", - "@redstone-finance/sdk": "^0.5.1" + "typechain": "^8.3.2", + "typescript": "^5.2.2", + "node-fetch": "^3.3.2", + "web3": "^4.9.0" } } \ No newline at end of file diff --git a/scripts/src/coinbase/data.json b/scripts/src/coinbase/data.json new file mode 100644 index 0000000..fc65a7d --- /dev/null +++ b/scripts/src/coinbase/data.json @@ -0,0 +1,54 @@ +{ + "BTC": { + "message": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000667021bc00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000f527b32780000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034254430000000000000000000000000000000000000000000000000000000000", + "signature": "0xfe72fff2d36008b1ad1c2f4bedd2457922ead78ba27b2cbdce6759a2bd833420966064a6df58df6a2d38c5283c7247abd68bff21cf3d237006e5bd0216bc76bc000000000000000000000000000000000000000000000000000000000000001c" + }, + "ETH": { + "message": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000667021bc00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000d22e27000000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034554480000000000000000000000000000000000000000000000000000000000", + "signature": "0xec8905696b28efa8e0b1715d0066d90ec6488915a8db5dc134d94bb11af5ec9ea95755f82e3b38f7a133b8629156ab4f71f1816c475db72554d6001ec1ec4b96000000000000000000000000000000000000000000000000000000000000001b" + }, + "XTZ": { + "message": "0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000006670218000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000bc7a000000000000000000000000000000000000000000000000000000000000000067072696365730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000358545a0000000000000000000000000000000000000000000000000000000000", + "signature": "0x8d86bf14f9e635caab4300780a7e830a372fee09ea61223793910b9533a0e7eb695ac2e43c36aa32ac26d501a272849ea800e1b13294fe26a10746f0be46542c000000000000000000000000000000000000000000000000000000000000001c" + }, + "DAI": { + "message": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000062cedf1c00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000f41780000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034441490000000000000000000000000000000000000000000000000000000000", + "signature": "0xad99f6564beb896c16f31145b5b3f176c7ac9ac1bba315d42ef252c460b7a0a9cd75d5e25e7d05d0616d4341a1f93c46168c487addf04414d7ab1d7b44ea4887000000000000000000000000000000000000000000000000000000000000001c" + }, + "REP": { + "message": "0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000006424597800000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000788b600000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035245500000000000000000000000000000000000000000000000000000000000", + "signature": "0x3df12304d9ac17111ac80a7237ec2fa994e1fc7b56547f12e4a66f30b4e60ff6f3adc3cc845993832855c4796b44e09b82770c7d888684eb5ae0695548e41dd8000000000000000000000000000000000000000000000000000000000000001b" + }, + "ZRX": { + "message": "0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000006670218000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000005d7330000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a52580000000000000000000000000000000000000000000000000000000000", + "signature": "0x2268a2c9c7939008468c5456906f8d2abba88e91b498782b17e53607c8512c2ff85c6f89ee9d7b2d559cb6e4b36d82d7858f866c73edb82f3b6a6e2695cd29cb000000000000000000000000000000000000000000000000000000000000001c" + }, + "BAT": { + "message": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000062cf049c00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000005a7b20000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034241540000000000000000000000000000000000000000000000000000000000", + "signature": "0x882bc7b0c91e58ca72e5689789463fad2772de1cd4b58c3e3cde43c4a3519fb3dd4e15256dffa71504048afa784bd288570aa98a7a660c54ef147256b302281e000000000000000000000000000000000000000000000000000000000000001c" + }, + "KNC": { + "message": "0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000006670210800000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000009d2100000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034b4e430000000000000000000000000000000000000000000000000000000000", + "signature": "0x51bdd17d0094d50a53b7d4e87bc85e7135c958945203016aebefccf40dc5f7b3c16f2b163bf3f8c40b4ace3336146614d7a7433a121be14934fb0f11407886bc000000000000000000000000000000000000000000000000000000000000001b" + }, + "LINK": { + "message": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000667021bc00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000da7f8c0000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044c494e4b00000000000000000000000000000000000000000000000000000000", + "signature": "0xb26581f2666663e06e715e4ce71f7deb4c96ff767bba5b5d998c572ece609685e8f8e9fb4f7dd7017d5ca4c8eba5335e45da4ad4e00b6fb3fad7b0912e54457a000000000000000000000000000000000000000000000000000000000000001b" + }, + "COMP": { + "message": "0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000006670218000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000031640b0000000000000000000000000000000000000000000000000000000000000000670726963657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004434f4d5000000000000000000000000000000000000000000000000000000000", + "signature": "0x0d26681fb760c2b8cfa540d94b2d829e3711c4ee86cfbc9c9c044e75bffa39339293af1b40b5dbe878bf2bd98d4a3c3e83afd79a16ca591fc51847fe8cebac19000000000000000000000000000000000000000000000000000000000000001b" + }, + "UNI": { + "message": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000667021bc00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000a63e98000000000000000000000000000000000000000000000000000000000000000670726963657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003554e490000000000000000000000000000000000000000000000000000000000", + "signature": "0x5b6d356b3d4c6b9dadfd4807e45e434cbecd6cab3e741191a1c6ef830b4aa0a52cceba6adfece1e0dbdeed1d4c9322d6b4a82e4bdc7e9e7f3ab25d6a5289f019000000000000000000000000000000000000000000000000000000000000001c" + }, + "GRT": { + "message": "0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000667021bc00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000034db40000000000000000000000000000000000000000000000000000000000000006707269636573000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034752540000000000000000000000000000000000000000000000000000000000", + "signature": "0x94bc918d38446f273ca07d91d9f1fff8bb0f2fcb31eb726ef26a32464e74ab07824edd51d32d9c343fef801c60d8731f4be8c6b287b956975d9b8998d56be7a6000000000000000000000000000000000000000000000000000000000000001b" + }, + "SNX": { + "message": "0x00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000066701fdc00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000001f5964000000000000000000000000000000000000000000000000000000000000000670726963657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003534e580000000000000000000000000000000000000000000000000000000000", + "signature": "0xe4e42dccf098cff55a1ef70bd97e0c2f3808e3a47b12b1187d9a3da65f2f3410e50edb6937210671f3a85168a95a44b031e113132cd281d40f4506862d31c72f000000000000000000000000000000000000000000000000000000000000001b" + } +} \ No newline at end of file diff --git a/scripts/src/coinbase/fetchData.js b/scripts/src/coinbase/fetchData.js new file mode 100644 index 0000000..05d5e53 --- /dev/null +++ b/scripts/src/coinbase/fetchData.js @@ -0,0 +1,67 @@ +require("dotenv").config(); // Add dotenv package to load environment variables +const fs = require("fs"); +const path = require("path"); +const crypto = require("crypto"); +const web3 = require("web3"); + +const { COINBASE_API_KEY, COINBASE_API_SECRET, COINBASE_API_PASSPHRASE } = + process.env; +if (!COINBASE_API_KEY || !COINBASE_API_SECRET || !COINBASE_API_PASSPHRASE) { + console.log( + "error: missing one or more of COINBASE_API_KEY, COINBASE_API_SECRET, COINBASE_API_PASSPHRASE environment variables" + ); + process.exit(1); +} + +const API_URL = "https://api.exchange.coinbase.com"; + +async function main() { + const timestamp = (new Date().getTime() / 1000).toString(); + const message = timestamp + "GET" + "/oracle"; + const hmac = crypto + .createHmac("sha256", Buffer.from(COINBASE_API_SECRET, "base64")) + .update(message); + const signature = hmac.digest("base64"); + + const headers = { + "CB-ACCESS-SIGN": signature, + "CB-ACCESS-TIMESTAMP": timestamp, + "CB-ACCESS-KEY": COINBASE_API_KEY, + "CB-ACCESS-PASSPHRASE": COINBASE_API_PASSPHRASE, + }; + + const res = await fetch(API_URL + "/oracle", { method: "GET", headers }); + + const { messages, signatures } = await res.json(); + + const output = {}; + for (let i = 0; i < messages.length; ++i) { + const record = Object.values( + web3.eth.abi.decodeParameters( + ["string", "uint", "string", "uint"], + messages[i] + ) + ).slice(0, -1); + + const adr = web3.eth.accounts.recover( + web3.utils.keccak256(messages[i]), + signatures[i] + ); + + if (adr !== "0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC") + throw new Error("Invalid signature"); + + output[record[2]] = { + message: messages[i], + signature: signatures[i], + }; + } + + const filePath = path.join(__dirname, "data.json"); + + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + + fs.writeFileSync(filePath, JSON.stringify(output, null, 2)); +} + +main(); diff --git a/scripts/src/coinbase/readData.js b/scripts/src/coinbase/readData.js new file mode 100644 index 0000000..353a9c0 --- /dev/null +++ b/scripts/src/coinbase/readData.js @@ -0,0 +1,37 @@ +require("dotenv").config(); // Load environment variables from .env file +const web3 = require("web3"); +const fs = require("fs"); +const path = require("path"); + +async function main(symbol) { + try { + if (!symbol) { + console.error("Error: No symbol argument provided"); + process.exit(1); + } + + const filePath = path.join(__dirname, "data.json"); + + const file = fs.readFileSync(filePath); + const data = JSON.parse(file); + const tickerData = data[symbol]; + + if (!tickerData) { + console.error("Error: Symbol not found"); + process.exit(1); + } + + const encodedData = web3.eth.abi.encodeParameters( + ["bytes", "bytes"], + [tickerData.message, tickerData.signature] + ); + + process.stdout.write(encodedData); + } catch (error) { + console.error("An error occurred:", error.message); + process.exit(1); + } +} + +const symbolArg = process.argv[2]; +main(symbolArg); diff --git a/scripts/yarn.lock b/scripts/yarn.lock index 75e7e53..24ace75 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -2,6 +2,16 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@^1.8.8": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" @@ -344,6 +354,18 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@noble/curves@1.4.0", "@noble/curves@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6" + integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + "@redstone-finance/oracles-smartweave-contracts@^0.5.1": version "0.5.1" resolved "https://registry.yarnpkg.com/@redstone-finance/oracles-smartweave-contracts/-/oracles-smartweave-contracts-0.5.1.tgz#fa68f76fb9258e4f037cde4f03b0ad87e8a8e8a2" @@ -378,6 +400,28 @@ ethers "^5.7.2" zod "^3.22.4" +"@scure/base@~1.1.6": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.7.tgz#fe973311a5c6267846aa131bc72e96c5d40d2b30" + integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== + +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== + dependencies: + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + "@typechain/ethers-v5@^11.1.2": version "11.1.2" resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-11.1.2.tgz#82510c1744f37a2f906b9e0532ac18c0b74ffe69" @@ -386,6 +430,13 @@ lodash "^4.17.15" ts-essentials "^7.0.1" +"@types/node@*": + version "20.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" + integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== + dependencies: + undici-types "~5.26.4" + "@types/node@^20.8.6": version "20.8.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.7.tgz#ad23827850843de973096edfc5abc9e922492a25" @@ -398,6 +449,18 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== +"@types/ws@8.5.3": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + +abitype@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745" + integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -432,6 +495,13 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + axios@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f" @@ -483,6 +553,17 @@ brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== +call-bind@^1.0.2, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -561,6 +642,23 @@ consola@^2.15.3: resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +crc-32@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + debug@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -578,15 +676,24 @@ deep-extend@~0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -dotenv@^16.3.1: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== elliptic@6.5.4: version "6.5.4" @@ -601,11 +708,33 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +ethereum-cryptography@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.0.tgz#06e2d9c0d89f98ffc6a83818f55bf85afecd50dc" + integrity sha512-hsm9JhfytIf8QME/3B7j4bc8V+VdTU+Vas1aJlvIS96ffoNAosudXvGoEvWmc7QZYdkC8mrMJz9r0fcbw7GyCA== + dependencies: + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" @@ -642,6 +771,19 @@ ethers@^5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + find-replace@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" @@ -659,6 +801,13 @@ follow-redirects@^1.15.6: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -668,6 +817,13 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -682,6 +838,22 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -694,6 +866,13 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -709,6 +888,30 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + 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" @@ -717,6 +920,13 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -739,6 +949,38 @@ inherits@2, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -800,6 +1042,27 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-fetch@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -812,6 +1075,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + prettier@^2.3.1: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" @@ -832,6 +1100,23 @@ scrypt-js@3.0.1: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + string-format@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" @@ -861,6 +1146,11 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-command-line-args@^2.2.0: version "2.5.1" resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" @@ -912,11 +1202,269 @@ undici-types@~5.25.1: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.25.3.tgz#e044115914c85f0bcbb229f346ab739f064998c3" integrity sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + +web3-core@^4.3.0, web3-core@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.5.0.tgz#f16e7f5bfa6373c7be45f0ed233aff479fd33079" + integrity sha512-Q8LIAqmF7vkRydBPiU+OC7wI44nEU6JEExolFaOakqrjMtQ1CWFHRUQMNJRDsk5bRirjyShuAsuqLeYByvvXhg== + dependencies: + web3-errors "^1.2.0" + web3-eth-accounts "^4.1.2" + web3-eth-iban "^4.0.7" + web3-providers-http "^4.1.0" + web3-providers-ws "^4.0.7" + web3-types "^1.7.0" + web3-utils "^4.3.0" + web3-validator "^2.0.6" + optionalDependencies: + web3-providers-ipc "^4.0.7" + +web3-errors@^1.1.3, web3-errors@^1.1.4, web3-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.2.0.tgz#441acfd7fd744c9beedf23f277f20759fae92433" + integrity sha512-58Kczou5zyjcm9LuSs5Hrm6VrG8t9p2J8X0yGArZrhKNPZL66gMGkOUpPx+EopE944Sk4yE+Q25hKv4H5BH+kA== + dependencies: + web3-types "^1.6.0" + +web3-eth-abi@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.2.tgz#d7592e2cc113fd34da3fb4c933537ddf8639d9b2" + integrity sha512-akbGi642UtKG3k3JuLbhl9KuG7LM/cXo/by2WfdwfOptGZrzRsWJNWje1d2xfw1n9kkVG9SAMvPJl1uSyR3dfw== + dependencies: + abitype "0.7.1" + web3-errors "^1.2.0" + web3-types "^1.6.0" + web3-utils "^4.3.0" + web3-validator "^2.0.6" + +web3-eth-accounts@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8" + integrity sha512-y0JynDeTDnclyuE9mShXLeEj+BCrPHxPHOyPCgTchUBQsALF9+0OhP7WiS3IqUuu0Hle5bjG2f5ddeiPtNEuLg== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + crc-32 "^1.2.2" + ethereum-cryptography "^2.0.0" + web3-errors "^1.1.4" + web3-types "^1.6.0" + web3-utils "^4.2.3" + web3-validator "^2.0.5" + +web3-eth-contract@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.5.0.tgz#954c9cf4c055f3f2c33189bdd69093a8e20a569a" + integrity sha512-AX6OiDrIryz/T28k9Xz0gXpUrlOUjcooEgGluu2s5dFDWCPM/zlN5RsUZlXZiXpQyj52VCUy5+bkvu3yDPA4fg== + dependencies: + web3-core "^4.4.0" + web3-errors "^1.2.0" + web3-eth "^4.7.0" + web3-eth-abi "^4.2.2" + web3-types "^1.6.0" + web3-utils "^4.3.0" + web3-validator "^2.0.6" + +web3-eth-ens@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.3.0.tgz#f44f279a4a07eae36e3de8384989ed069201a795" + integrity sha512-QpiKT9GqJouH5kEI/pRFprh88YPCtbht2Ym6rrklZ+VoWl9D+wLfbwvW7Aox349FS7k0UX2qVins5tVNLJ5GCQ== + dependencies: + "@adraffy/ens-normalize" "^1.8.8" + web3-core "^4.4.0" + web3-errors "^1.2.0" + web3-eth "^4.7.0" + web3-eth-contract "^4.5.0" + web3-net "^4.1.0" + web3-types "^1.6.0" + web3-utils "^4.3.0" + web3-validator "^2.0.6" + +web3-eth-iban@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz#ee504f845d7b6315f0be78fcf070ccd5d38e4aaf" + integrity sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ== + dependencies: + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-eth-personal@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz#b51628c560de550ca8b354fa784f9556aae6065c" + integrity sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw== + dependencies: + web3-core "^4.3.0" + web3-eth "^4.3.1" + web3-rpc-methods "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-eth@^4.3.1, web3-eth@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.7.0.tgz#7d18815c7a79c200552bd0df8d3127f7532b3cc2" + integrity sha512-gqlWq4Xjz+yKL2MdxQ+BgR3F4CRo4AXWDXzftb3LDzvauEfjk/yRyoxkMSK4S9RIG96ylRImS172cV6cYzcukw== + dependencies: + setimmediate "^1.0.5" + web3-core "^4.4.0" + web3-errors "^1.2.0" + web3-eth-abi "^4.2.2" + web3-eth-accounts "^4.1.2" + web3-net "^4.1.0" + web3-providers-ws "^4.0.7" + web3-rpc-methods "^1.3.0" + web3-types "^1.6.0" + web3-utils "^4.3.0" + web3-validator "^2.0.6" + +web3-net@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.1.0.tgz#db7bde675e58b153339e4f149f29ec0410d6bab2" + integrity sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA== + dependencies: + web3-core "^4.4.0" + web3-rpc-methods "^1.3.0" + web3-types "^1.6.0" + web3-utils "^4.3.0" + +web3-providers-http@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.1.0.tgz#8d7afda67d1d8542ca85b30f60a3d1fe1993b561" + integrity sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg== + dependencies: + cross-fetch "^4.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + +web3-providers-ipc@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz#9ec4c8565053af005a5170ba80cddeb40ff3e3d3" + integrity sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g== + dependencies: + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + +web3-providers-ws@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz#7a78a0dcf077e0e802da524fbb37d080b356c14b" + integrity sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w== + dependencies: + "@types/ws" "8.5.3" + isomorphic-ws "^5.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + ws "^8.8.1" + +web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670" + integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig== + dependencies: + web3-core "^4.4.0" + web3-types "^1.6.0" + web3-validator "^2.0.6" + +web3-types@^1.3.0, web3-types@^1.6.0, web3-types@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.7.0.tgz#9945fa644af96b20b1db18564aff9ab8db00df59" + integrity sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA== + +web3-utils@^4.0.7, web3-utils@^4.2.3, web3-utils@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.0.tgz#c18918f0d692f745d622d22172406f6102528860" + integrity sha512-fGG2IZr0XB1vEoWZiyJzoy28HpsIfZgz4mgPeQA9aj5rIx8z0o80qUPtIyrCYX/Bo2gYALlV5SWIJWxJNUQn9Q== + dependencies: + ethereum-cryptography "^2.0.0" + eventemitter3 "^5.0.1" + web3-errors "^1.2.0" + web3-types "^1.6.0" + web3-validator "^2.0.6" + +web3-validator@^2.0.3, web3-validator@^2.0.5, web3-validator@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248" + integrity sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg== + dependencies: + ethereum-cryptography "^2.0.0" + util "^0.12.5" + web3-errors "^1.2.0" + web3-types "^1.6.0" + zod "^3.21.4" + +web3@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/web3/-/web3-4.9.0.tgz#2d18f6a48f366601aef32308260542ed14bd2b1b" + integrity sha512-O0R90ijjyqUlG1Wk3SXqfYMU1ZGJvLCAF/WfSg/isDz/0Fkpqxoj893wauZ+ieRvTXITlbQHVXGfpp8qrhWZ1g== + dependencies: + web3-core "^4.4.0" + web3-errors "^1.2.0" + web3-eth "^4.7.0" + web3-eth-abi "^4.2.2" + web3-eth-accounts "^4.1.2" + web3-eth-contract "^4.5.0" + web3-eth-ens "^4.3.0" + web3-eth-iban "^4.0.7" + web3-eth-personal "^4.0.8" + web3-net "^4.1.0" + web3-providers-http "^4.1.0" + web3-providers-ws "^4.0.7" + web3-rpc-methods "^1.3.0" + web3-types "^1.6.0" + web3-utils "^4.3.0" + web3-validator "^2.0.6" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + wordwrapjs@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" @@ -935,7 +1483,12 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -zod@^3.22.4: +ws@^8.8.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + +zod@^3.21.4, zod@^3.22.4: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== diff --git a/src/adapters/source-adapters/CoinbaseSourceAdapter.sol b/src/adapters/source-adapters/CoinbaseSourceAdapter.sol index 928a186..ea8c594 100644 --- a/src/adapters/source-adapters/CoinbaseSourceAdapter.sol +++ b/src/adapters/source-adapters/CoinbaseSourceAdapter.sol @@ -56,7 +56,7 @@ abstract contract CoinbaseSourceAdapter is DiamondRootOval { * @return updatedAt The timestamp of the answer. */ function getLatestSourceData() public view virtual override returns (int256, uint256) { - (, int256 sourceAnswer,, uint256 updatedAt,) = COINBASE_SOURCE.latestRoundData(TICKER); + (, int256 sourceAnswer, uint256 updatedAt) = COINBASE_SOURCE.latestRoundData(TICKER); return (DecimalLib.convertDecimals(sourceAnswer, SOURCE_DECIMALS, 18), updatedAt); } @@ -73,7 +73,7 @@ abstract contract CoinbaseSourceAdapter is DiamondRootOval { // Tries getting the latest data as of the requested timestamp. If this is not possible, // returns the earliest data available past the requested timestamp considering the maxTraversal limitations. function _tryLatestRoundDataAt(uint256 timestamp, uint256 maxTraversal) internal view returns (int256, uint256) { - (uint80 roundId, int256 answer,, uint256 updatedAt,) = COINBASE_SOURCE.latestRoundData(TICKER); + (uint80 roundId, int256 answer, uint256 updatedAt) = COINBASE_SOURCE.latestRoundData(TICKER); // If the latest update is older than or equal to the requested timestamp, return the latest data. if (updatedAt <= timestamp) { @@ -101,7 +101,7 @@ abstract contract CoinbaseSourceAdapter is DiamondRootOval { int256 answer; uint256 updatedAt; for (uint80 i = 1; i <= maxTraversal && latestRoundId >= i; i++) { - (, answer,, updatedAt,) = COINBASE_SOURCE.getRoundData(TICKER, latestRoundId - i); + (, answer, updatedAt) = COINBASE_SOURCE.getRoundData(TICKER, latestRoundId - i); if (updatedAt <= timestamp) { return (answer, updatedAt); } diff --git a/src/interfaces/coinbase/IAggregatorV3SourceCoinbase.sol b/src/interfaces/coinbase/IAggregatorV3SourceCoinbase.sol index 157a184..aba39f4 100644 --- a/src/interfaces/coinbase/IAggregatorV3SourceCoinbase.sol +++ b/src/interfaces/coinbase/IAggregatorV3SourceCoinbase.sol @@ -7,10 +7,10 @@ interface IAggregatorV3SourceCoinbase { function latestRoundData(string memory ticker) external view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); + returns (uint80 roundId, int256 answer, uint256 updatedAt); function getRoundData(string memory ticker, uint80 _roundId) external view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); + returns (uint80 roundId, int256 answer, uint256 updatedAt); } diff --git a/src/oracles/CoinbaseOracle.sol b/src/oracles/CoinbaseOracle.sol index 9517a58..ea708a4 100644 --- a/src/oracles/CoinbaseOracle.sol +++ b/src/oracles/CoinbaseOracle.sol @@ -9,13 +9,17 @@ import {IAggregatorV3SourceCoinbase} from "../interfaces/coinbase/IAggregatorV3S */ contract CoinbaseOracle is IAggregatorV3SourceCoinbase { address immutable reporter; - uint8 public immutable decimals; + string public dataKind; + + struct RoundData { + int256 answer; + uint256 timestamp; + } struct PriceData { uint80 lastRoundId; - mapping(uint80 => int256) roundAnswers; - mapping(uint80 => uint256) roundTimestamps; + mapping(uint80 => RoundData) rounds; } mapping(string => PriceData) private prices; @@ -25,11 +29,13 @@ contract CoinbaseOracle is IAggregatorV3SourceCoinbase { /** * @notice Constructor to initialize the CoinbaseOracle contract. * @param _decimals The number of decimals in the reported price. + * @param _dataKind The kind of data that the oracle will report, expected to be "prices" but open for changes. * @param _reporter The address of the reporter allowed to push price data. */ - constructor(uint8 _decimals, address _reporter) { + constructor(uint8 _decimals, string memory _dataKind, address _reporter) { decimals = _decimals; reporter = _reporter; + dataKind = _dataKind; } /** @@ -37,19 +43,16 @@ contract CoinbaseOracle is IAggregatorV3SourceCoinbase { * @param ticker The ticker symbol to retrieve the data for. * @return roundId The ID of the latest round. * @return answer The latest price. - * @return startedAt The timestamp when the round started. - * @return updatedAt The timestamp when the round was updated. - * @return answeredInRound The round ID in which the answer was computed. + * @return updatedAt The timestamp when the price was updated. */ function latestRoundData(string memory ticker) external view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + returns (uint80 roundId, int256 answer, uint256 updatedAt) { PriceData storage priceData = prices[ticker]; - int256 latestAnswer = priceData.roundAnswers[priceData.lastRoundId]; - uint256 latestTimestamp = priceData.roundTimestamps[priceData.lastRoundId]; - return (priceData.lastRoundId, latestAnswer, latestTimestamp, latestTimestamp, priceData.lastRoundId); + RoundData storage latestRound = priceData.rounds[priceData.lastRoundId]; + return (priceData.lastRoundId, latestRound.answer, latestRound.timestamp); } /** @@ -58,46 +61,47 @@ contract CoinbaseOracle is IAggregatorV3SourceCoinbase { * @param roundId The round ID to retrieve the data for. * @return roundId The ID of the round. * @return answer The price of the round. - * @return startedAt The timestamp when the round started. * @return updatedAt The timestamp when the round was updated. - * @return answeredInRound The round ID in which the answer was computed. */ function getRoundData(string memory ticker, uint80 roundId) external view - returns (uint80, int256, uint256, uint256, uint80) + returns (uint80, int256 answer, uint256 updatedAt) { PriceData storage priceData = prices[ticker]; - int256 latestAnswer = priceData.roundAnswers[roundId]; - uint256 latestTimestamp = priceData.roundTimestamps[roundId]; - return (roundId, latestAnswer, latestTimestamp, latestTimestamp, roundId); + RoundData memory round = priceData.rounds[roundId]; + return (roundId, round.answer, round.timestamp); } /** * @notice Pushes a new price to the oracle for a given ticker. - * @param priceData The encoded price data. + * @dev Only the designated reporter can push price data. + * @param priceData The encoded price data, which contains the following fields: + * - kind: A string representing the kind of data (e.g., "prices"). + * - timestamp: A uint256 representing the timestamp when the price was reported (e.g., 1629350000). + * - ticker: A string representing the ticker symbol of the asset (e.g., "BTC"). + * - price: A uint256 representing the price of the asset (with 6 decimals). * @param signature The signature to verify the authenticity of the data. */ function pushPrice(bytes memory priceData, bytes memory signature) external { ( - string memory kind, // e.g. "price" + string memory kind, // e.g. "prices" uint256 timestamp, // e.g. 1629350000 string memory ticker, // e.g. "BTC" uint256 price // 6 decimals ) = abi.decode(priceData, (string, uint256, string, uint256)); - require(keccak256(abi.encodePacked(kind)) == keccak256(abi.encodePacked("price")), "Invalid kind."); + require(keccak256(abi.encodePacked(kind)) == keccak256(abi.encodePacked(dataKind)), "Invalid kind."); PriceData storage priceDataStruct = prices[ticker]; - uint256 latestTimestamp = priceDataStruct.roundTimestamps[priceDataStruct.lastRoundId]; + uint256 latestTimestamp = priceDataStruct.rounds[priceDataStruct.lastRoundId].timestamp; require(timestamp > latestTimestamp, "Invalid timestamp."); require(recoverSigner(priceData, signature) == reporter, "Invalid signature."); - require(price < uint256(type(int256).max), "Price exceeds max value."); + require(price <= uint256(type(int256).max), "Price exceeds max value."); priceDataStruct.lastRoundId++; - priceDataStruct.roundAnswers[priceDataStruct.lastRoundId] = int256(price); - priceDataStruct.roundTimestamps[priceDataStruct.lastRoundId] = timestamp; + priceDataStruct.rounds[priceDataStruct.lastRoundId] = RoundData(int256(price), timestamp); emit PricePushed(ticker, priceDataStruct.lastRoundId, int256(price), timestamp); } diff --git a/test/unit/CoinbaseOracle.sol b/test/unit/CoinbaseOracle.sol index 4a20c6f..91854ca 100644 --- a/test/unit/CoinbaseOracle.sol +++ b/test/unit/CoinbaseOracle.sol @@ -1,44 +1,100 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - import {CommonTest} from "../Common.sol"; -import {BaseController} from "../../src/controllers/BaseController.sol"; -import {CoinbaseSourceAdapter} from "../../src/adapters/source-adapters/CoinbaseSourceAdapter.sol"; -import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; -import {IAggregatorV3SourceCoinbase} from "../../src/interfaces/coinbase/IAggregatorV3SourceCoinbase.sol"; -import {CoinbaseOracle} from "../../src/oracles/CoinbaseOracle.sol"; +import {IAggregatorV3} from "src/interfaces/chainlink/IAggregatorV3.sol"; +import {CoinbaseOracle} from "src/oracles/CoinbaseOracle.sol"; -contract CoinbaseSourceAdapterTest is CommonTest { - CoinbaseOracle coinbaseOracle; +contract CoinbaseOracleTest is CommonTest { + CoinbaseOracle public coinbaseOracle; + address public constant coinbaseProdReporter = 0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC; address public reporter; uint256 public reporterPk; - string public constant ethTicker = "ETH"; - string public constant btcTicker = "BTC"; + + string[] public tickers; function setUp() public { + coinbaseOracle = new CoinbaseOracle(6, "prices", coinbaseProdReporter); + tickers = new string[](13); + tickers[0] = "BTC"; + tickers[1] = "ETH"; + tickers[2] = "XTZ"; + tickers[3] = "DAI"; + tickers[4] = "REP"; + tickers[5] = "ZRX"; + tickers[6] = "BAT"; + tickers[7] = "KNC"; + tickers[8] = "LINK"; + tickers[9] = "COMP"; + tickers[10] = "UNI"; + tickers[11] = "GRT"; + tickers[12] = "SNX"; + (address _reporter, uint256 _reporterPk) = makeAddrAndKey("reporter"); reporter = _reporter; reporterPk = _reporterPk; - coinbaseOracle = new CoinbaseOracle(6, reporter); + coinbaseOracle = new CoinbaseOracle(6, "prices", reporter); + } + + function testPushPricesProd() public { + coinbaseOracle = new CoinbaseOracle(6, "prices", coinbaseProdReporter); + string[] memory fetchCommands = new string[](3); + fetchCommands[0] = "node"; + fetchCommands[1] = "--no-warnings"; + fetchCommands[2] = "./scripts/src/coinbase/fetchData.js"; + + // Fetch is optional, feel free to uncomment the following line + // if commented, the test will use the data from ./scripts/src/coinbase/data.json + // If you want to fetch the data, you need to have a valid .env file in scripts/ folder (see readme.md) + // vm.ffi(fetchCommands); + + for (uint256 i = 0; i < tickers.length; i++) { + string[] memory readCommands = new string[](4); + readCommands[0] = "node"; + readCommands[1] = "--no-warnings"; + readCommands[2] = "./scripts/src/coinbase/readData.js"; + readCommands[3] = tickers[i]; + bytes memory apiData = vm.ffi(readCommands); + + (bytes memory data, bytes memory signature) = abi.decode(apiData, (bytes, bytes)); + + ( + , + /* string memory kind */ + // e.g. "prices" + uint256 timestamp, // e.g. 1629350000 + string memory ticker, // e.g. "BTC" + uint256 price // 6 decimals + ) = abi.decode(data, (string, uint256, string, uint256)); + + coinbaseOracle.pushPrice(data, signature); + + ( + , + /* uint80 roundId */ + int256 answer, + uint256 updatedAt + ) = coinbaseOracle.latestRoundData(ticker); + + assertEq(uint256(answer), price); + assertEq(updatedAt, timestamp); + } } function testPushPriceETH() public { - _testPushPrice(ethTicker, 10e6); + _testPushPrice(tickers[1], 10e6); } function testPushPriceBTC() public { - _testPushPrice(btcTicker, 20e6); + _testPushPrice(tickers[0], 20e6); } function testPushPriceBothTickers() public { - _testPushPrice(ethTicker, 10e6); + _testPushPrice(tickers[1], 10e6); vm.warp(block.timestamp + 1); - _testPushPrice(btcTicker, 20e6); + _testPushPrice(tickers[0], 20e6); } function _testPushPrice(string memory ticker, uint256 price) internal { - string memory kind = "price"; + string memory kind = "prices"; uint256 timestamp = block.timestamp; bytes memory encodedData = abi.encode(kind, timestamp, ticker, price); @@ -51,7 +107,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { coinbaseOracle.pushPrice(encodedData, signature); - (, int256 answer, uint256 updatedAt,,) = coinbaseOracle.latestRoundData(ticker); + (, int256 answer, uint256 updatedAt) = coinbaseOracle.latestRoundData(ticker); assertEq(uint256(answer), price); assertEq(updatedAt, timestamp); diff --git a/test/unit/CoinbaseSourceAdapter.sol b/test/unit/CoinbaseSourceAdapter.sol index a6324cb..842c5e7 100644 --- a/test/unit/CoinbaseSourceAdapter.sol +++ b/test/unit/CoinbaseSourceAdapter.sol @@ -24,7 +24,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { uint256 public price = 3000e6; function pushPrice(string memory ticker, uint256 priceToPush, uint256 timestamp) public { - string memory kind = "price"; + string memory kind = "prices"; bytes memory encodedData = abi.encode(kind, timestamp, ticker, priceToPush); @@ -45,7 +45,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { (address _reporter, uint256 _reporterPk) = makeAddrAndKey("reporter"); reporter = _reporter; reporterPk = _reporterPk; - coinbase = new CoinbaseOracle(6, reporter); + coinbase = new CoinbaseOracle(6, "prices", reporter); sourceAdapter = new TestedSourceAdapter(IAggregatorV3SourceCoinbase(address(coinbase)), ticker); // Push some prices to the oracle @@ -60,7 +60,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { } function testCorrectlyStandardizesOutputs() public { - (, int256 latestCoinbasePrice,, uint256 latestCoinbaseTimestamp,) = coinbase.latestRoundData(ticker); + (, int256 latestCoinbasePrice, uint256 latestCoinbaseTimestamp) = coinbase.latestRoundData(ticker); (int256 latestSourceAnswer, uint256 latestSourceTimestamp) = sourceAdapter.getLatestSourceData(); assertTrue(scaleCoinbaseTo18(latestCoinbasePrice) == latestSourceAnswer); @@ -68,12 +68,12 @@ contract CoinbaseSourceAdapterTest is CommonTest { } function testCorrectlyLooksBackThroughRounds() public { - (uint80 latestRound, int256 latestAnswer,, uint256 latestUpdatedAt,) = coinbase.latestRoundData(ticker); + (uint80 latestRound, int256 latestAnswer, uint256 latestUpdatedAt) = coinbase.latestRoundData(ticker); assertTrue(uint256(latestAnswer) == price - 1500); uint256 targetTime = block.timestamp - 1 hours; (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); - (, int256 answer, uint256 startedAt,,) = coinbase.getRoundData(ticker, latestRound - 1); + (, int256 answer, uint256 startedAt) = coinbase.getRoundData(ticker, latestRound - 1); assertTrue(startedAt <= targetTime); // The time from the chainlink source is at least 1 hours old. assertTrue(scaleCoinbaseTo18(answer) == lookBackPrice); assertTrue(uint256(answer) == (price - 1000)); @@ -82,7 +82,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { // Next, try looking back 2 hours. Equally, we should get the price from 2 rounds ago. targetTime = block.timestamp - 2 hours; (lookBackPrice, lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); - (, answer, startedAt,,) = coinbase.getRoundData(ticker, latestRound - 2); + (, answer, startedAt) = coinbase.getRoundData(ticker, latestRound - 2); assertTrue(startedAt <= targetTime); // The time from the chainlink source is at least 2 hours old. assertTrue(scaleCoinbaseTo18(answer) == lookBackPrice); assertTrue(uint256(answer) == (price - 500)); @@ -102,8 +102,8 @@ contract CoinbaseSourceAdapterTest is CommonTest { // If we try look back longer than this we should get the price from round 2, no matter how far we look back. uint256 targetTime = block.timestamp - 2 hours; (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 2); - (uint80 latestRound,,,,) = coinbase.latestRoundData(ticker); - (, int256 answer, uint256 startedAt,,) = coinbase.getRoundData(ticker, latestRound - 2); + (uint80 latestRound,,) = coinbase.latestRoundData(ticker); + (, int256 answer, uint256 startedAt) = coinbase.getRoundData(ticker, latestRound - 2); assertTrue(scaleCoinbaseTo18(answer) == lookBackPrice); assertTrue(startedAt == lookBackTimestamp); @@ -120,7 +120,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { } function testNonHistoricalData() public { - coinbase = new CoinbaseOracle(6, reporter); + coinbase = new CoinbaseOracle(6, "prices", reporter); sourceAdapter = new TestedSourceAdapter(IAggregatorV3SourceCoinbase(address(coinbase)), ticker); // Push only one price to the oracle @@ -129,7 +129,7 @@ contract CoinbaseSourceAdapterTest is CommonTest { uint256 targetTime = block.timestamp - 1 hours; - (, int256 answer,, uint256 updatedAt,) = coinbase.latestRoundData(ticker); + (uint80 roundId, int256 answer, uint256 updatedAt) = coinbase.latestRoundData(ticker); (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 0); assertEq(lookBackPrice, scaleCoinbaseTo18(answer));