From bce772df026a8f9188d0f69c8625d96ec0f2e879 Mon Sep 17 00:00:00 2001
From: Chris Maree
Date: Tue, 18 Jun 2024 16:29:35 +0200
Subject: [PATCH 1/9] Update README.md (#27)
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index f1c48b4..cb8e74f 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,13 @@
-Oval is an MEV capture mechanism that lets protocols reclaim Oracle Extractable Value(OEV) by auctioning oracle updates. It leveraged Flashbot's [MEV-share](https://docs.flashbots.net/flashbots-protect/mev-share) OFA system by running auctions for the right to backrun an oracle update.
+Oval is an MEV capture mechanism that lets protocols reclaim Oracle Extractable Value(OEV) by auctioning oracle updates. It leverages Flashbot's [MEV-share](https://docs.flashbots.net/flashbots-protect/mev-share) OFA system by running auctions for the right to backrun an oracle update.
For more information on how Oval works and how to integrate with it see the [docs](https://docs.oval.xyz/).
## Repo contents
-This repository contains the main smart contracts for Oval. It uses [Foundry](https://github.com/foundry-rs/foundry). For spesific information on contracts, see the [Contract Architecture](https://docs.oval.xyz/contract-architecture) section of the docs.
+This repository contains the main smart contracts for Oval. It uses [Foundry](https://github.com/foundry-rs/foundry). For specific information on contracts, see the [Contract Architecture](https://docs.oval.xyz/contract-architecture) section of the docs.
This repo also consists of a set of scripts used to profile the Oval gas usage. See [README](./scripts/README.md) that shows how to run these and [this](https://docs.oval.xyz/contract-architecture/gas-profiling) docs page that outlines the gas profiling finding.
@@ -32,10 +32,10 @@ forge build
This repository uses foundry fork tests. You will need to run the fork tests as follows:
```
-export RPC_MAINNET=[your ethereum mainnet archive node url]
+export RPC_MAINNET=[your Ethereum mainnet archive node url]
forge test
```
## License
-All code in this repository is licensed under BUSL-1.1 unless specified differently in the file. Individual exceptions to this license can be made by Risk Labs, which holds the rights to this software and design. If you are interested in using the code or designs in a derivative work, feel free to reach out to licensing@risklabs.foundation.
\ No newline at end of file
+All code in this repository is licensed under BUSL-1.1 unless specified differently in the file. Individual exceptions to this license can be made by Risk Labs, which holds the rights to this software and design. If you are interested in using the code or designs in a derivative work, feel free to reach out to licensing@risklabs.foundation.
From 2aa2c7d133b4dd034cc90f729ee8d5640cc8bc35 Mon Sep 17 00:00:00 2001
From: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com>
Date: Tue, 18 Jun 2024 17:56:31 +0300
Subject: [PATCH 2/9] M-03 [Oval] Newest Prices May Be Reported Ignoring the
lockWindow() Constraint (#17)
Signed-off-by: Reinis Martinsons
---
src/controllers/ImmutableController.sol | 3 +++
src/controllers/MutableUnlockersController.sol | 3 +++
src/factories/BaseFactory.sol | 2 ++
3 files changed, 8 insertions(+)
diff --git a/src/controllers/ImmutableController.sol b/src/controllers/ImmutableController.sol
index 0b48665..a42bbd7 100644
--- a/src/controllers/ImmutableController.sol
+++ b/src/controllers/ImmutableController.sol
@@ -18,6 +18,9 @@ abstract contract ImmutableController is Oval {
mapping(address => bool) public unlockers;
constructor(uint256 _lockWindow, uint256 _maxTraversal, address[] memory _unlockers, uint256 _maxAge) {
+ require(_maxAge > _lockWindow, "Max age not above lock window");
+ require(_maxTraversal > 0, "Max traversal must be > 0");
+
LOCK_WINDOW = _lockWindow;
MAX_TRAVERSAL = _maxTraversal;
MAX_AGE = _maxAge;
diff --git a/src/controllers/MutableUnlockersController.sol b/src/controllers/MutableUnlockersController.sol
index a7acf13..c9ea029 100644
--- a/src/controllers/MutableUnlockersController.sol
+++ b/src/controllers/MutableUnlockersController.sol
@@ -16,6 +16,9 @@ abstract contract MutableUnlockersController is Ownable, Oval {
mapping(address => bool) public unlockers;
constructor(uint256 _lockWindow, uint256 _maxTraversal, address[] memory _unlockers, uint256 _maxAge) {
+ require(_maxAge > _lockWindow, "Max age not above lock window");
+ require(_maxTraversal > 0, "Max traversal must be > 0");
+
LOCK_WINDOW = _lockWindow;
MAX_TRAVERSAL = _maxTraversal;
MAX_AGE = _maxAge;
diff --git a/src/factories/BaseFactory.sol b/src/factories/BaseFactory.sol
index f4fcd8c..a97b33b 100644
--- a/src/factories/BaseFactory.sol
+++ b/src/factories/BaseFactory.sol
@@ -24,6 +24,8 @@ contract BaseFactory is Ownable {
);
constructor(uint256 _maxTraversal, address[] memory _defaultUnlockers) {
+ require(_maxTraversal > 0, "Max traversal must be > 0");
+
MAX_TRAVERSAL = _maxTraversal;
setDefaultUnlockers(_defaultUnlockers);
}
From d977126453d5f51bd99a0f1dc26ebf393c1409c3 Mon Sep 17 00:00:00 2001
From: Pablo Maldonado
Date: Tue, 18 Jun 2024 15:58:28 +0100
Subject: [PATCH 3/9] N-10 [Oval] Typographical Errors (#23)
Signed-off-by: Pablo Maldonado
---
src/controllers/MutableUnlockersController.sol | 2 +-
src/factories/PermissionProxy.sol | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/controllers/MutableUnlockersController.sol b/src/controllers/MutableUnlockersController.sol
index c9ea029..af33590 100644
--- a/src/controllers/MutableUnlockersController.sol
+++ b/src/controllers/MutableUnlockersController.sol
@@ -5,7 +5,7 @@ import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
import {Oval} from "../Oval.sol";
/**
- * @title MutableUnlockersController is a controller that only allows unlockers to be change, but other params are immutable.
+ * @title MutableUnlockersController is a controller that only allows unlockers to be changed, but other params are immutable.
*/
abstract contract MutableUnlockersController is Ownable, Oval {
// these don't need to be public since they can be accessed via the accessor functions below.
diff --git a/src/factories/PermissionProxy.sol b/src/factories/PermissionProxy.sol
index dbd9a75..03282b1 100644
--- a/src/factories/PermissionProxy.sol
+++ b/src/factories/PermissionProxy.sol
@@ -5,9 +5,9 @@ import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
import {Multicall} from "openzeppelin-contracts/contracts/utils/Multicall.sol";
/**
- * @title PermissionProxy is a proxy that allows extends the permissions given to it to a configurable set
+ * @title PermissionProxy is a proxy that extends the permissions given to it to a configurable set
* of addresses.
- * @dev The intended use case for this contract is to add this as a single unlocker to oval contracts, allowing the
+ * @dev The intended use case for this contract is to add this as a single unlocker to Oval contracts, allowing the
* owner of this contract to delegate that permission to many different unlocker addresses.
*/
contract PermissionProxy is Ownable, Multicall {
From 0e9423521dd62d13ac52941e6dc4ea51ccece6ff Mon Sep 17 00:00:00 2001
From: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com>
Date: Tue, 18 Jun 2024 18:05:40 +0300
Subject: [PATCH 4/9] M-04 [Oval] Updating Parameters Inside the BaseController
Contract May Lead to Undesired Outcomes (#19)
Signed-off-by: Reinis Martinsons
---
src/controllers/BaseController.sol | 24 ++++++++++++++--
test/fork/adapters/UnionSourceAdapter.sol | 2 +-
test/unit/BaseController.sol | 2 ++
test/unit/SnapshotSource.SnapshotData.sol | 35 ++++++++++++-----------
4 files changed, 42 insertions(+), 21 deletions(-)
diff --git a/src/controllers/BaseController.sol b/src/controllers/BaseController.sol
index 36a30dd..16b4246 100644
--- a/src/controllers/BaseController.sol
+++ b/src/controllers/BaseController.sol
@@ -46,13 +46,13 @@ abstract contract BaseController is Ownable, Oval {
* @param newLockWindow The lockWindow to set.
*/
function setLockWindow(uint256 newLockWindow) public onlyOwner {
+ require(maxAge() > newLockWindow, "Max age not above lock window");
+
(int256 currentAnswer, uint256 currentTimestamp,) = internalLatestData();
lockWindow_ = newLockWindow;
- // Compare Oval results so that change in lock window does not change returned data.
- (int256 newAnswer, uint256 newTimestamp,) = internalLatestData();
- require(currentAnswer == newAnswer && currentTimestamp == newTimestamp, "Must unlock first");
+ _checkDataNotChanged(currentAnswer, currentTimestamp);
emit LockWindowSet(newLockWindow);
}
@@ -62,8 +62,14 @@ abstract contract BaseController is Ownable, Oval {
* @param newMaxTraversal The maxTraversal to set.
*/
function setMaxTraversal(uint256 newMaxTraversal) public onlyOwner {
+ require(newMaxTraversal > 0, "Max traversal must be > 0");
+
+ (int256 currentAnswer, uint256 currentTimestamp,) = internalLatestData();
+
maxTraversal_ = newMaxTraversal;
+ _checkDataNotChanged(currentAnswer, currentTimestamp);
+
emit MaxTraversalSet(newMaxTraversal);
}
@@ -72,8 +78,14 @@ abstract contract BaseController is Ownable, Oval {
* @param newMaxAge The maxAge to set
*/
function setMaxAge(uint256 newMaxAge) public onlyOwner {
+ require(newMaxAge > lockWindow(), "Max age not above lock window");
+
+ (int256 currentAnswer, uint256 currentTimestamp,) = internalLatestData();
+
maxAge_ = newMaxAge;
+ _checkDataNotChanged(currentAnswer, currentTimestamp);
+
emit MaxAgeSet(newMaxAge);
}
@@ -101,4 +113,10 @@ abstract contract BaseController is Ownable, Oval {
function maxAge() public view override returns (uint256) {
return maxAge_;
}
+
+ // Helper function to ensure that changing controller parameters does not change the returned data.
+ function _checkDataNotChanged(int256 currentAnswer, uint256 currentTimestamp) internal view {
+ (int256 newAnswer, uint256 newTimestamp,) = internalLatestData();
+ require(currentAnswer == newAnswer && currentTimestamp == newTimestamp, "Must unlock first");
+ }
}
diff --git a/test/fork/adapters/UnionSourceAdapter.sol b/test/fork/adapters/UnionSourceAdapter.sol
index ca0e14b..e04c80e 100644
--- a/test/fork/adapters/UnionSourceAdapter.sol
+++ b/test/fork/adapters/UnionSourceAdapter.sol
@@ -165,9 +165,9 @@ contract UnionSourceAdapterTest is CommonTest {
function testLookbackDropChronicle() public {
// Fork to a block where chronicle was the newest.
vm.createSelectFork("mainnet", targetChronicleBlock);
+ _whitelistOnChronicle();
uint256 targetTimestamp = block.timestamp;
sourceAdapter.setMaxAge(2 days); // Set max age to 2 days to disable this logic for the test.
- _whitelistOnChronicle();
// Snapshotting union adapter should not affect historical lookups, but we do it just to prove it does not interfere.
sourceAdapter.snapshotData();
diff --git a/test/unit/BaseController.sol b/test/unit/BaseController.sol
index 7a2083c..b00abe6 100644
--- a/test/unit/BaseController.sol
+++ b/test/unit/BaseController.sol
@@ -16,6 +16,8 @@ contract BaseControllerTest is CommonTest {
TestBaseController baseController;
function setUp() public {
+ vm.warp(lastUnlockTime);
+
vm.startPrank(owner);
baseController = new TestBaseController(18);
baseController.setUnlocker(permissionedUnlocker, true);
diff --git a/test/unit/SnapshotSource.SnapshotData.sol b/test/unit/SnapshotSource.SnapshotData.sol
index f5b501f..576e939 100644
--- a/test/unit/SnapshotSource.SnapshotData.sol
+++ b/test/unit/SnapshotSource.SnapshotData.sol
@@ -78,38 +78,39 @@ contract SnapshotSourceSnapshotDataTest is CommonTest {
function testMaxAgeIsRespected() public {
// Set maxAge to 2000 for testing
+ vm.warp(10000);
snapshotSource.setMaxAge(2000);
// Publish data at different timestamps
- vm.warp(1000);
- snapshotSource.publishSourceData(100, 1000);
+ vm.warp(11000);
+ snapshotSource.publishSourceData(100, 11000);
snapshotSource.snapshotData();
- vm.warp(2000);
- snapshotSource.publishSourceData(200, 2000);
+ vm.warp(12000);
+ snapshotSource.publishSourceData(200, 12000);
snapshotSource.snapshotData();
- vm.warp(3000);
- snapshotSource.publishSourceData(300, 3000);
+ vm.warp(13000);
+ snapshotSource.publishSourceData(300, 13000);
snapshotSource.snapshotData();
- vm.warp(4000);
- snapshotSource.publishSourceData(400, 4000);
+ vm.warp(14000);
+ snapshotSource.publishSourceData(400, 14000);
snapshotSource.snapshotData();
// Verify behavior when requesting data within the maxAge limit
- (int256 answerAt4000, uint256 timestampAt4000,) = snapshotSource.tryLatestDataAt(4000, 10);
- assertTrue(answerAt4000 == 400 && timestampAt4000 == 4000);
+ (int256 answerAt14000, uint256 timestampAt14000,) = snapshotSource.tryLatestDataAt(14000, 10);
+ assertTrue(answerAt14000 == 400 && timestampAt14000 == 14000);
- (int256 answerAt3000, uint256 timestampAt3000,) = snapshotSource.tryLatestDataAt(3000, 10);
- assertTrue(answerAt3000 == 300 && timestampAt3000 == 3000);
+ (int256 answerAt13000, uint256 timestampAt13000,) = snapshotSource.tryLatestDataAt(13000, 10);
+ assertTrue(answerAt13000 == 300 && timestampAt13000 == 13000);
// Request data at the limit of maxAge should still work.
- (int256 answerAt2000, uint256 timestampAt2000,) = snapshotSource.tryLatestDataAt(2000, 10);
- assertTrue(answerAt2000 == 200 && timestampAt2000 == 2000);
+ (int256 answerAt12000, uint256 timestampAt12000,) = snapshotSource.tryLatestDataAt(12000, 10);
+ assertTrue(answerAt12000 == 200 && timestampAt12000 == 12000);
- // Request data older than maxAge (1000), should get the latest available data at 4000.
- (int256 answerAt1000, uint256 timestampAt1000,) = snapshotSource.tryLatestDataAt(1000, 10);
- assertTrue(answerAt1000 == 400 && timestampAt1000 == 4000);
+ // Request data older than maxAge (1000), should get the latest available data at 14000.
+ (int256 answerAt11000, uint256 timestampAt11000,) = snapshotSource.tryLatestDataAt(11000, 10);
+ assertTrue(answerAt11000 == 400 && timestampAt11000 == 14000);
}
}
From 77e544175a897a28efcecad2c51b09240c01d257 Mon Sep 17 00:00:00 2001
From: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com>
Date: Tue, 18 Jun 2024 18:08:17 +0300
Subject: [PATCH 5/9] L-05 [Oval] PermissionProxy.execute Will Fail if Non-Zero
value Is Provided (#20)
Signed-off-by: Reinis Martinsons
---
src/factories/PermissionProxy.sol | 9 ++++-----
test/unit/PermissionProxy.sol | 15 +++++++--------
2 files changed, 11 insertions(+), 13 deletions(-)
diff --git a/src/factories/PermissionProxy.sol b/src/factories/PermissionProxy.sol
index 03282b1..0b6ccd2 100644
--- a/src/factories/PermissionProxy.sol
+++ b/src/factories/PermissionProxy.sol
@@ -12,7 +12,7 @@ import {Multicall} from "openzeppelin-contracts/contracts/utils/Multicall.sol";
*/
contract PermissionProxy is Ownable, Multicall {
error SenderNotApproved(address sender);
- error CallFailed(address target, uint256 value, bytes callData);
+ error CallFailed(address target, bytes callData);
event SenderSet(address sender, bool allowed);
@@ -32,20 +32,19 @@ contract PermissionProxy is Ownable, Multicall {
* @notice Executes a call from this contract.
* @dev Can only be called by an allowed sender.
* @param target the address to call.
- * @param value the value to send.
* @param callData the calldata to use for the call.
* @return the data returned by the external call.
*
*/
- function execute(address target, uint256 value, bytes memory callData) external returns (bytes memory) {
+ function execute(address target, bytes memory callData) external returns (bytes memory) {
if (!senders[msg.sender]) {
revert SenderNotApproved(msg.sender);
}
- (bool success, bytes memory returnData) = target.call{value: value}(callData);
+ (bool success, bytes memory returnData) = target.call{value: 0}(callData);
if (!success) {
- revert CallFailed(target, value, callData);
+ revert CallFailed(target, callData);
}
return returnData;
diff --git a/test/unit/PermissionProxy.sol b/test/unit/PermissionProxy.sol
index fac8ca6..5a83f86 100644
--- a/test/unit/PermissionProxy.sol
+++ b/test/unit/PermissionProxy.sol
@@ -8,7 +8,6 @@ contract PermissionProxyTest is CommonTest {
PermissionProxy permissionProxy;
address mockAddress = address(0xdeadbeef);
bytes testCallData = abi.encodeWithSignature("foo()");
- uint256 testValue = 1;
bytes returnData = abi.encode(uint256(7));
function setUp() public {
@@ -19,22 +18,22 @@ contract PermissionProxyTest is CommonTest {
function testSenderPermissions() public {
vm.prank(account2);
vm.expectRevert(abi.encodeWithSelector(PermissionProxy.SenderNotApproved.selector, account2));
- permissionProxy.execute(mockAddress, testValue, testCallData);
+ permissionProxy.execute(mockAddress, testCallData);
vm.prank(account1);
- vm.mockCall(mockAddress, testValue, testCallData, abi.encode(uint256(7)));
- vm.expectCall(mockAddress, testValue, testCallData);
- bytes memory actualReturnValue = permissionProxy.execute(mockAddress, testValue, testCallData);
+ vm.mockCall(mockAddress, testCallData, abi.encode(uint256(7)));
+ vm.expectCall(mockAddress, testCallData);
+ bytes memory actualReturnValue = permissionProxy.execute(mockAddress, testCallData);
assertEq0(actualReturnValue, returnData);
}
function testCallFailed() public {
vm.prank(account1);
- vm.mockCallRevert(mockAddress, testValue, testCallData, "");
+ vm.mockCallRevert(mockAddress, testCallData, "");
vm.expectRevert(
- abi.encodeWithSelector(PermissionProxy.CallFailed.selector, mockAddress, testValue, testCallData)
+ abi.encodeWithSelector(PermissionProxy.CallFailed.selector, mockAddress, testCallData)
);
- permissionProxy.execute(mockAddress, testValue, testCallData);
+ permissionProxy.execute(mockAddress, testCallData);
}
function testSetSender() public {
From 70e5b663265bf3c1a479a8b223d0472760426bee Mon Sep 17 00:00:00 2001
From: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com>
Date: Tue, 18 Jun 2024 18:10:24 +0300
Subject: [PATCH 6/9] L-06 [Oval] CoinbaseSourceAdapter May Return Old Prices
(#24)
Signed-off-by: Reinis Martinsons
---
src/adapters/source-adapters/CoinbaseSourceAdapter.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/adapters/source-adapters/CoinbaseSourceAdapter.sol b/src/adapters/source-adapters/CoinbaseSourceAdapter.sol
index 928a186..67a003c 100644
--- a/src/adapters/source-adapters/CoinbaseSourceAdapter.sol
+++ b/src/adapters/source-adapters/CoinbaseSourceAdapter.sol
@@ -83,8 +83,8 @@ abstract contract CoinbaseSourceAdapter is DiamondRootOval {
// Attempt traversing historical round data backwards from roundId.
(int256 historicalAnswer, uint256 historicalUpdatedAt) = _searchRoundDataAt(timestamp, roundId, maxTraversal);
- // Validate returned data. If it is uninitialized, fall back to returning the current latest round data.
- if (historicalUpdatedAt > 0) {
+ // Validate returned data. If it is uninitialized or too old, fall back to returning the current latest round data.
+ if (historicalUpdatedAt > block.timestamp - maxAge()) {
return (historicalAnswer, historicalUpdatedAt);
}
From 5624e604c2346226547d462fbdb445788c090331 Mon Sep 17 00:00:00 2001
From: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com>
Date: Tue, 18 Jun 2024 18:11:20 +0300
Subject: [PATCH 7/9] N-13 [Oval] Misleading Comment (#25)
Signed-off-by: Reinis Martinsons
---
src/adapters/source-adapters/ChainlinkSourceAdapter.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/adapters/source-adapters/ChainlinkSourceAdapter.sol b/src/adapters/source-adapters/ChainlinkSourceAdapter.sol
index fe3dbf9..e44bd95 100644
--- a/src/adapters/source-adapters/ChainlinkSourceAdapter.sol
+++ b/src/adapters/source-adapters/ChainlinkSourceAdapter.sol
@@ -93,7 +93,7 @@ abstract contract ChainlinkSourceAdapter is DiamondRootOval {
(int256 historicalAnswer, uint256 historicalUpdatedAt, uint80 historicalRoundId) =
_searchRoundDataAt(timestamp, roundId, maxTraversal);
- // Validate returned data. If it is uninitialized we fallback to returning the current latest round data.
+ // Validate returned data. If it is uninitialized or too old, fall back to returning the current latest round data.
if (historicalUpdatedAt > block.timestamp - maxAge()) {
return (historicalAnswer, historicalUpdatedAt, historicalRoundId);
}
From 6f531729e50761541114b8107994d7fd901dc172 Mon Sep 17 00:00:00 2001
From: Reinis Martinsons <77973553+Reinis-FRP@users.noreply.github.com>
Date: Tue, 18 Jun 2024 18:13:30 +0300
Subject: [PATCH 8/9] N-14 [Oval] Possible Duplicate Event Emission (#26)
Signed-off-by: Reinis Martinsons
---
src/controllers/BaseController.sol | 2 ++
src/controllers/MutableUnlockersController.sol | 2 ++
2 files changed, 4 insertions(+)
diff --git a/src/controllers/BaseController.sol b/src/controllers/BaseController.sol
index 16b4246..a903115 100644
--- a/src/controllers/BaseController.sol
+++ b/src/controllers/BaseController.sol
@@ -23,6 +23,8 @@ abstract contract BaseController is Ownable, Oval {
* @param allowed The unlocker status to set.
*/
function setUnlocker(address unlocker, bool allowed) public onlyOwner {
+ require(unlockers[unlocker] != allowed, "Unlocker not changed");
+
unlockers[unlocker] = allowed;
emit UnlockerSet(unlocker, allowed);
diff --git a/src/controllers/MutableUnlockersController.sol b/src/controllers/MutableUnlockersController.sol
index af33590..1a28310 100644
--- a/src/controllers/MutableUnlockersController.sol
+++ b/src/controllers/MutableUnlockersController.sol
@@ -39,6 +39,8 @@ abstract contract MutableUnlockersController is Ownable, Oval {
* @param allowed The unlocker status to set.
*/
function setUnlocker(address unlocker, bool allowed) public onlyOwner {
+ require(unlockers[unlocker] != allowed, "Unlocker not changed");
+
unlockers[unlocker] = allowed;
emit UnlockerSet(unlocker, allowed);
From ff31b1bdd52da472fed40f93970e1886f4f27cef Mon Sep 17 00:00:00 2001
From: Pablo Maldonado
Date: Tue, 18 Jun 2024 17:00:13 +0100
Subject: [PATCH 9/9] M-02 [Oval] Attempts to Push Price to the CoinbaseOracle
Will Always Fail (#18)
Signed-off-by: Pablo Maldonado
---
scripts/.env.example | 3 +
scripts/README.md | 40 ++
scripts/package.json | 12 +-
scripts/src/coinbase/data.json | 54 ++
scripts/src/coinbase/fetchData.js | 67 +++
scripts/src/coinbase/readData.js | 37 ++
scripts/yarn.lock | 563 +++++++++++++++++-
.../source-adapters/CoinbaseSourceAdapter.sol | 6 +-
.../coinbase/IAggregatorV3SourceCoinbase.sol | 4 +-
src/oracles/CoinbaseOracle.sol | 72 ++-
test/mocks/MockCoinbaseOracle.sol | 16 +
test/unit/CoinbaseOracle.sol | 98 ++-
test/unit/CoinbaseSourceAdapter.sol | 21 +-
13 files changed, 917 insertions(+), 76 deletions(-)
create mode 100644 scripts/src/coinbase/data.json
create mode 100644 scripts/src/coinbase/fetchData.js
create mode 100644 scripts/src/coinbase/readData.js
create mode 100644 test/mocks/MockCoinbaseOracle.sol
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 67a003c..030ba83 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..38e65fe 100644
--- a/src/oracles/CoinbaseOracle.sol
+++ b/src/oracles/CoinbaseOracle.sol
@@ -8,28 +8,38 @@ import {IAggregatorV3SourceCoinbase} from "../interfaces/coinbase/IAggregatorV3S
* @notice A smart contract that serves as an oracle for price data reported by a designated reporter.
*/
contract CoinbaseOracle is IAggregatorV3SourceCoinbase {
- address immutable reporter;
+ address public immutable REPORTER = 0xfCEAdAFab14d46e20144F48824d0C09B1a03F2BC;
+ uint8 public immutable DECIMALS = 6;
+ bytes32 public immutable KIND_HASH = keccak256(abi.encodePacked("prices"));
- uint8 public immutable decimals;
+ 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;
+ mapping(string => PriceData) internal prices;
event PricePushed(string indexed ticker, uint80 indexed roundId, int256 price, uint256 timestamp);
/**
- * @notice Constructor to initialize the CoinbaseOracle contract.
- * @param _decimals The number of decimals in the reported price.
- * @param _reporter The address of the reporter allowed to push price data.
+ * @notice Returns the number of decimals used by the oracle.
+ * @return The number of decimals used by the oracle.
+ */
+ function decimals() external pure returns (uint8) {
+ return DECIMALS;
+ }
+
+ /**
+ * @notice Returns the address of the reporter.
+ * @return The address of the reporter.
*/
- constructor(uint8 _decimals, address _reporter) {
- decimals = _decimals;
- reporter = _reporter;
+ function reporter() public view virtual returns (address) {
+ return REPORTER;
}
/**
@@ -37,19 +47,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 +65,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)) == KIND_HASH, "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(recoverSigner(priceData, signature) == reporter(), "Invalid signature.");
+ 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/mocks/MockCoinbaseOracle.sol b/test/mocks/MockCoinbaseOracle.sol
new file mode 100644
index 0000000..d712d6a
--- /dev/null
+++ b/test/mocks/MockCoinbaseOracle.sol
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity 0.8.17;
+
+import {CoinbaseOracle} from "../../src/oracles/CoinbaseOracle.sol";
+
+contract MockCoinbaseOracle is CoinbaseOracle {
+ address public customReporter;
+
+ constructor(address _customReporter) {
+ customReporter = _customReporter;
+ }
+
+ function reporter() public view override returns (address) {
+ return customReporter;
+ }
+}
diff --git a/test/unit/CoinbaseOracle.sol b/test/unit/CoinbaseOracle.sol
index 4a20c6f..36b7fb8 100644
--- a/test/unit/CoinbaseOracle.sol
+++ b/test/unit/CoinbaseOracle.sol
@@ -1,44 +1,104 @@
-// 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";
+import {MockCoinbaseOracle} from "../mocks/MockCoinbaseOracle.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();
+ 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();
+ }
+
+ function testPushPricesProd() public {
+ coinbaseOracle = new CoinbaseOracle();
+ 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);
+ coinbaseOracle = new MockCoinbaseOracle(reporter);
+ _testPushPrice(tickers[1], 10e6);
}
function testPushPriceBTC() public {
- _testPushPrice(btcTicker, 20e6);
+ coinbaseOracle = new MockCoinbaseOracle(reporter);
+ _testPushPrice(tickers[0], 20e6);
}
function testPushPriceBothTickers() public {
- _testPushPrice(ethTicker, 10e6);
+ coinbaseOracle = new MockCoinbaseOracle(reporter);
+ _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 +111,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..815b154 100644
--- a/test/unit/CoinbaseSourceAdapter.sol
+++ b/test/unit/CoinbaseSourceAdapter.sol
@@ -8,6 +8,7 @@ import {CoinbaseSourceAdapter} from "../../src/adapters/source-adapters/Coinbase
import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol";
import {IAggregatorV3SourceCoinbase} from "../../src/interfaces/coinbase/IAggregatorV3SourceCoinbase.sol";
import {CoinbaseOracle} from "../../src/oracles/CoinbaseOracle.sol";
+import {MockCoinbaseOracle} from "../mocks/MockCoinbaseOracle.sol";
contract TestedSourceAdapter is CoinbaseSourceAdapter, BaseController {
constructor(IAggregatorV3SourceCoinbase source, string memory ticker) CoinbaseSourceAdapter(source, ticker) {}
@@ -24,7 +25,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 +46,7 @@ contract CoinbaseSourceAdapterTest is CommonTest {
(address _reporter, uint256 _reporterPk) = makeAddrAndKey("reporter");
reporter = _reporter;
reporterPk = _reporterPk;
- coinbase = new CoinbaseOracle(6, reporter);
+ coinbase = new MockCoinbaseOracle(reporter);
sourceAdapter = new TestedSourceAdapter(IAggregatorV3SourceCoinbase(address(coinbase)), ticker);
// Push some prices to the oracle
@@ -60,7 +61,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 +69,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 +83,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 +103,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 +121,7 @@ contract CoinbaseSourceAdapterTest is CommonTest {
}
function testNonHistoricalData() public {
- coinbase = new CoinbaseOracle(6, reporter);
+ coinbase = new MockCoinbaseOracle(reporter);
sourceAdapter = new TestedSourceAdapter(IAggregatorV3SourceCoinbase(address(coinbase)), ticker);
// Push only one price to the oracle
@@ -129,7 +130,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));