From 4339ec071d8da0cc58359cb8898758876f15302e Mon Sep 17 00:00:00 2001 From: MathisGD Date: Sat, 24 Jun 2023 19:18:58 +0200 Subject: [PATCH 001/112] feat: first working version --- .gitmodules | 3 + foundry.toml | 3 + lib/solmate | 1 + src/Market.sol | 166 ++++++++++++++++ src/interfaces/IERC20.sol | 7 + src/interfaces/IOracle.sol | 6 + src/libraries/MathLib.sol | 33 ++++ src/libraries/SafeTransferLib.sol | 74 +++++++ src/mocks/ERC20Mock.sol | 12 ++ src/mocks/OracleMock.sol | 12 ++ test/forge/Market.t.sol | 313 ++++++++++++++++++++++++++++++ test/hardhat/Market.spec.ts | 90 +++++++++ 12 files changed, 720 insertions(+) create mode 160000 lib/solmate create mode 100644 src/Market.sol create mode 100644 src/interfaces/IERC20.sol create mode 100644 src/interfaces/IOracle.sol create mode 100644 src/libraries/MathLib.sol create mode 100644 src/libraries/SafeTransferLib.sol create mode 100644 src/mocks/ERC20Mock.sol create mode 100644 src/mocks/OracleMock.sol create mode 100644 test/forge/Market.t.sol create mode 100644 test/hardhat/Market.spec.ts diff --git a/.gitmodules b/.gitmodules index 888d42dcd..c59f396e6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/solmate"] + path = lib/solmate + url = https://github.com/transmissions11/solmate diff --git a/foundry.toml b/foundry.toml index fb9adcbbf..af26ce773 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,4 +4,7 @@ out = "out" libs = ["lib"] via-ir = true +[fmt] +int_types = "short" + # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/lib/solmate b/lib/solmate new file mode 160000 index 000000000..bfc9c2586 --- /dev/null +++ b/lib/solmate @@ -0,0 +1 @@ +Subproject commit bfc9c25865a274a7827fea5abf6e4fb64fc64e6c diff --git a/src/Market.sol b/src/Market.sol new file mode 100644 index 000000000..fefe23c22 --- /dev/null +++ b/src/Market.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC20} from "src/interfaces/IERC20.sol"; +import {IOracle} from "src/interfaces/IOracle.sol"; + +import {MathLib} from "src/libraries/MathLib.sol"; +import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; + +uint constant N = 10; + +function bucketToLLTV(uint bucket) pure returns (uint) { + return MathLib.wDiv(bucket, N - 1); +} + +function irm(uint utilization) pure returns (uint) { + // Divide by the number of seconds in a year. + // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. + return utilization / 365 days; +} + +contract Market { + using MathLib for int; + using MathLib for uint; + using SafeTransferLib for IERC20; + + // Constants. + + uint public constant getN = N; + + address public immutable borrowableAsset; + address public immutable collateralAsset; + address public immutable borrowableOracle; + address public immutable collateralOracle; + + // Storage. + + // User' supply balances. + mapping(address => mapping(uint => uint)) public supplyShare; + // User' borrow balances. + mapping(address => mapping(uint => uint)) public borrowShare; + // User' collateral balance. + mapping(address => uint) public collateral; + // Market total supply. + mapping(uint => uint) public totalSupply; + // Market total supply shares. + mapping(uint => uint) public totalSupplyShares; + // Market total borrow. + mapping(uint => uint) public totalBorrow; + // Market total borrow shares. + mapping(uint => uint) public totalBorrowShares; + // Interests last update. + uint public lastUpdate; + + // Constructor. + + constructor( + address newBorrowableAsset, + address newCollateralAsset, + address newBorrowableOracle, + address newCollateralOracle + ) { + borrowableAsset = newBorrowableAsset; + collateralAsset = newCollateralAsset; + borrowableOracle = newBorrowableOracle; + collateralOracle = newCollateralOracle; + } + + // Suppliers position management. + + /// @dev positive amount to deposit. + function modifyDeposit(int amount, uint bucket) external { + if (amount == 0) return; + require(bucket < N, "unknown bucket"); + + accrueInterests(bucket); + + if (totalSupply[bucket] == 0) { + supplyShare[msg.sender][bucket] = 1e18; + totalSupplyShares[bucket] = 1e18; + } else { + int shares = amount.wMul(totalSupplyShares[bucket]).wDiv(totalSupply[bucket]); + supplyShare[msg.sender][bucket] = (int(supplyShare[msg.sender][bucket]) + shares).safeToUint(); + totalSupplyShares[bucket] = (int(totalSupplyShares[bucket]) + shares).safeToUint(); + } + + // No need to check if the integer is positive. + totalSupply[bucket] = uint(int(totalSupply[bucket]) + amount); + + if (amount < 0) require(totalBorrow[bucket] <= totalSupply[bucket], "not enough liquidity"); + + IERC20(borrowableAsset).handleTransfer({user: msg.sender, amountIn: amount}); + } + + // Borrowers position management. + + /// @dev positive amount to borrow (to discuss). + function modifyBorrow(int amount, uint bucket) external { + if (amount == 0) return; + require(bucket < N, "unknown bucket"); + + accrueInterests(bucket); + + if (totalBorrow[bucket] == 0) { + borrowShare[msg.sender][bucket] = 1e18; + totalBorrowShares[bucket] = 1e18; + } else { + int shares = amount.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); + borrowShare[msg.sender][bucket] = (int(borrowShare[msg.sender][bucket]) + shares).safeToUint(); + totalBorrowShares[bucket] = (int(totalBorrowShares[bucket]) + shares).safeToUint(); + } + + // No need to check if the integer is positive. + totalBorrow[bucket] = uint(int(totalBorrow[bucket]) + amount); + + if (amount > 0) { + checkHealth(msg.sender); + require(totalBorrow[bucket] <= totalSupply[bucket], "not enough liquidity"); + } + + IERC20(borrowableAsset).handleTransfer({user: msg.sender, amountIn: -amount}); + } + + /// @dev positive amount to deposit. + function modifyCollateral(int amount) external { + collateral[msg.sender] = (int(collateral[msg.sender]) + amount).safeToUint(); + + if (amount < 0) checkHealth(msg.sender); + + IERC20(collateralAsset).handleTransfer({user: msg.sender, amountIn: amount}); + } + + // Interests management. + + function accrueInterests(uint bucket) internal { + uint bucketTotalBorrow = totalBorrow[bucket]; + uint bucketTotalSupply = totalSupply[bucket]; + if (bucketTotalSupply == 0) return; + uint utilization = bucketTotalBorrow.wDiv(bucketTotalSupply); + uint borrowRate = irm(utilization); + uint accruedInterests = bucketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate); + + totalSupply[bucket] = bucketTotalSupply + accruedInterests; + totalBorrow[bucket] = bucketTotalBorrow + accruedInterests; + lastUpdate = block.timestamp; + } + + // Health check. + + function checkHealth(address user) public view { + // Temporary trick to ease testing. + if (IOracle(borrowableOracle).price() == 0) return; + uint collateralValueRequired; + for (uint bucket = 1; bucket < N; bucket++) { + if (totalBorrowShares[bucket] > 0 && borrowShare[user][bucket] > 0) { + uint borrowAtBucket = + borrowShare[user][bucket].wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]); + collateralValueRequired += + borrowAtBucket.wMul(IOracle(borrowableOracle).price()).wDiv(bucketToLLTV(bucket)); + } + } + require( + collateral[user].wMul(IOracle(collateralOracle).price()) >= collateralValueRequired, "not enough collateral" + ); + } +} diff --git a/src/interfaces/IERC20.sol b/src/interfaces/IERC20.sol new file mode 100644 index 000000000..73f30ca56 --- /dev/null +++ b/src/interfaces/IERC20.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +interface IERC20 { + function transferFrom(address, address, uint) external; + function transfer(address, uint) external; +} diff --git a/src/interfaces/IOracle.sol b/src/interfaces/IOracle.sol new file mode 100644 index 000000000..90f640c7c --- /dev/null +++ b/src/interfaces/IOracle.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +interface IOracle { + function price() external view returns (uint); +} diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol new file mode 100644 index 000000000..2ba196b34 --- /dev/null +++ b/src/libraries/MathLib.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +/// @notice Maths utils. +library MathLib { + uint internal constant WAD = 1e18; + + /// @dev Rounds down. + function wMul(uint x, uint y) internal pure returns (uint z) { + z = x * y / WAD; + } + + /// @dev Rounds down. + function wMul(int x, uint y) internal pure returns (int z) { + z = x * int(y) / int(WAD); + } + + /// @dev Rounds down. + function wDiv(uint x, uint y) internal pure returns (uint z) { + z = x * WAD / y; + } + + /// @dev Rounds down. + function wDiv(int x, uint y) internal pure returns (int z) { + z = x * int(WAD) / int(y); + } + + /// @dev Reverts if x is negative. + function safeToUint(int x) internal pure returns (uint z) { + require(x >= 0); + z = uint(x); + } +} diff --git a/src/libraries/SafeTransferLib.sol b/src/libraries/SafeTransferLib.sol new file mode 100644 index 000000000..1198bc41f --- /dev/null +++ b/src/libraries/SafeTransferLib.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import {IERC20} from "src/interfaces/IERC20.sol"; + +/// @notice Safe ERC20 transfer library that gracefully handles missing return values. +/// @dev Greatly inspired by Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol). +/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. +/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. +library SafeTransferLib { + function handleTransfer(IERC20 token, address user, int amountIn) internal { + if (amountIn > 0) return safeTransferFrom(token, user, address(this), uint(amountIn)); + return safeTransfer(token, user, uint(-amountIn)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint amount) private { + bool success; + + /// @solidity memory-safe-assembly + assembly { + // Get a pointer to some free memory. + let freeMemoryPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) + mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. + mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. + mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. + + success := + and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) + ) + } + + require(success, "TRANSFER_FROM_FAILED"); + } + + function safeTransfer(IERC20 token, address to, uint amount) private { + bool success; + + /// @solidity memory-safe-assembly + assembly { + // Get a pointer to some free memory. + let freeMemoryPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) + mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. + mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. + + success := + and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) + ) + } + + require(success, "TRANSFER_FAILED"); + } +} diff --git a/src/mocks/ERC20Mock.sol b/src/mocks/ERC20Mock.sol new file mode 100644 index 000000000..83ff194aa --- /dev/null +++ b/src/mocks/ERC20Mock.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {ERC20} from "solmate/tokens/ERC20.sol"; + +contract ERC20Mock is ERC20 { + constructor(string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol, _decimals) {} + + function setBalance(address owner, uint amount) external { + balanceOf[owner] = amount; + } +} diff --git a/src/mocks/OracleMock.sol b/src/mocks/OracleMock.sol new file mode 100644 index 000000000..e32e6d22b --- /dev/null +++ b/src/mocks/OracleMock.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IOracle} from "src/Market.sol"; + +contract OracleMock is IOracle { + uint public price; + + function setPrice(uint newPrice) external { + price = newPrice; + } +} diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol new file mode 100644 index 000000000..63f4c18cf --- /dev/null +++ b/test/forge/Market.t.sol @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import "src/Market.sol"; +import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; +import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; + +contract MarketTest is Test { + using MathLib for uint; + + Market private market; + ERC20 private borrowableAsset; + ERC20 private collateralAsset; + Oracle private borrowableOracle; + Oracle private collateralOracle; + + function setUp() public { + borrowableAsset = new ERC20("borrowable", "B", 18); + collateralAsset = new ERC20("collateral", "C", 18); + borrowableOracle = new Oracle(); + collateralOracle = new Oracle(); + market = new Market( + address(borrowableAsset), + address(collateralAsset), + address(borrowableOracle), + address(collateralOracle) + ); + + borrowableOracle.setPrice(0); + collateralOracle.setPrice(1e18); + } + + function invariantParams() public { + assertEq(market.borrowableAsset(), address(borrowableAsset)); + assertEq(market.collateralAsset(), address(collateralAsset)); + assertEq(market.borrowableOracle(), address(borrowableOracle)); + assertEq(market.collateralOracle(), address(collateralOracle)); + } + + function invariantLiquidity() public { + for (uint bucket; bucket < N; bucket++) { + assertLe(market.totalBorrow(bucket), market.totalSupply(bucket)); + } + } + + function invariantLiquidity2() public { + uint expectedMinimumBalance; + for (uint bucket; bucket < N; bucket++) { + expectedMinimumBalance += market.totalSupply(bucket) + market.totalBorrow(bucket); + } + assertGe(borrowableAsset.balanceOf(address(market)), expectedMinimumBalance); + } + + function testDeposit(address user, uint amount, uint maxLLTV) public { + amount = bound(amount, 1, 2 ** 64); + vm.assume(user != address(market)); + vm.assume(maxLLTV < N); + + borrowableAsset.setBalance(user, amount); + + vm.startPrank(user); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amount), maxLLTV); + vm.stopPrank(); + + assertEq(market.supplyShare(user, maxLLTV), 1e18, "supply balance user"); + assertEq(borrowableAsset.balanceOf(user), 0, "token balance user"); + assertEq(borrowableAsset.balanceOf(address(market)), amount, "token balance market"); + } + + function testBorrow(address lender, uint maxLLTV, uint amountLent, address borrower, uint amountBorrowed) public { + amountLent = bound(amountLent, 0, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); + vm.assume(lender != address(market)); + vm.assume(maxLLTV < N); + + borrowableAsset.setBalance(lender, amountLent); + + vm.startPrank(lender); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amountLent), maxLLTV); + vm.stopPrank(); + + if (amountBorrowed == 0) { + // No need to deposit any collateral as the price of the borrowed asset is 0. + market.modifyBorrow(int(amountBorrowed), maxLLTV); + return; + } + + if (amountBorrowed > amountLent) { + vm.prank(borrower); + vm.expectRevert("not enough liquidity"); + // No need to deposit any collateral as the price of the borrowed asset is 0. + market.modifyBorrow(int(amountBorrowed), maxLLTV); + return; + } + + vm.prank(borrower); + // No need to deposit any collateral as the price of the borrowed asset is 0. + market.modifyBorrow(int(amountBorrowed), maxLLTV); + + assertEq(market.borrowShare(borrower, maxLLTV), 1e18, "borrow balance borrower"); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed, "token balance borrower"); + assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed, "token balance market"); + } + + function testWithdraw( + address lender, + uint maxLLTV, + uint amountLent, + uint amountWithdrawn, + address borrower, + uint amountBorrowed + ) public { + amountLent = bound(amountLent, 1, 2 ** 64); + vm.assume(lender != address(market)); + vm.assume(lender != borrower); + vm.assume(maxLLTV < N); + vm.assume(amountLent >= amountBorrowed); + vm.assume(int(amountWithdrawn) >= 0); + + borrowableAsset.setBalance(lender, amountLent); + + vm.startPrank(lender); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amountLent), maxLLTV); + vm.stopPrank(); + + vm.startPrank(borrower); + market.modifyBorrow(int(amountBorrowed), maxLLTV); + vm.stopPrank(); + + // Should revert because not enough liquidity. + if (amountWithdrawn > amountLent - amountBorrowed) { + vm.prank(lender); + vm.expectRevert(); + market.modifyDeposit(-int(amountWithdrawn), maxLLTV); + return; + } + + vm.prank(lender); + market.modifyDeposit(-int(amountWithdrawn), maxLLTV); + + assertApproxEqAbs( + market.supplyShare(lender, maxLLTV), + (amountLent - amountWithdrawn) * 1e18 / amountLent, + 1e3, + "supply balance lender" + ); + assertEq(borrowableAsset.balanceOf(lender), amountWithdrawn, "token balance lender"); + assertEq( + borrowableAsset.balanceOf(address(market)), + amountLent - amountBorrowed - amountWithdrawn, + "token balance market" + ); + } + + function testCollateralRequirements( + address lender, + address borrower, + uint amountCollateral, + uint amountBorrowed, + uint priceCollateral, + uint priceBorrowable + ) public { + vm.assume(lender != address(market)); + vm.assume(borrower != address(market)); + vm.assume(lender != borrower); + amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); + priceBorrowable = bound(priceBorrowable, 1, 2 ** 64); + amountCollateral = bound(amountCollateral, 0, 2 ** 64); + priceCollateral = bound(priceCollateral, 1, 2 ** 64); + + borrowableOracle.setPrice(priceBorrowable); + collateralOracle.setPrice(priceCollateral); + + borrowableAsset.setBalance(lender, amountBorrowed); + collateralAsset.setBalance(borrower, amountCollateral); + + vm.startPrank(lender); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amountBorrowed), 1); + vm.stopPrank(); + + vm.startPrank(borrower); + collateralAsset.approve(address(market), type(uint).max); + market.modifyCollateral(int(amountCollateral)); + vm.stopPrank(); + + uint collateralValue = amountCollateral.wMul(priceCollateral); + uint borrowValue = amountBorrowed.wMul(priceBorrowable); + if (borrowValue == 0 || (collateralValue > 0 && borrowValue.wDiv(bucketToLLTV(1)) <= collateralValue)) { + vm.prank(borrower); + market.modifyBorrow(int(amountBorrowed), 1); + } else { + vm.prank(borrower); + vm.expectRevert("not enough collateral"); + market.modifyBorrow(int(amountBorrowed), 1); + } + } + + function testRepay( + address lender, + uint maxLLTV, + uint amountLent, + address borrower, + uint amountBorrowed, + uint amountRepaid + ) public { + amountLent = bound(amountLent, 1, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 1, amountLent); + amountRepaid = bound(amountRepaid, 0, amountBorrowed); + vm.assume(lender != address(market)); + vm.assume(lender != borrower); + vm.assume(maxLLTV < N); + + borrowableAsset.setBalance(lender, amountLent); + + vm.startPrank(lender); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amountLent), maxLLTV); + vm.stopPrank(); + + vm.startPrank(borrower); + market.modifyBorrow(int(amountBorrowed), maxLLTV); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyBorrow(-int(amountRepaid), maxLLTV); + vm.stopPrank(); + + assertApproxEqAbs( + market.borrowShare(borrower, maxLLTV), + (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, + 1e3, + "borrow balance borrower" + ); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid, "token balance borrower"); + assertEq( + borrowableAsset.balanceOf(address(market)), + amountLent - amountBorrowed + amountRepaid, + "token balance market" + ); + } + + function testDepositCollateral(address user, uint amount) public { + collateralAsset.setBalance(user, amount); + vm.assume(int(amount) >= 0); + vm.assume(user != address(market)); + + vm.startPrank(user); + collateralAsset.approve(address(market), type(uint).max); + market.modifyCollateral(int(amount)); + vm.stopPrank(); + + assertEq(market.collateral(user), amount); + assertEq(collateralAsset.balanceOf(user), 0); + assertEq(collateralAsset.balanceOf(address(market)), amount); + } + + function testWithdrawCollateral(address user, uint amountDeposited, uint amountWithdrawn) public { + vm.assume(amountDeposited >= amountWithdrawn); + vm.assume(int(amountDeposited) >= 0); + vm.assume(user != address(market)); + + collateralAsset.setBalance(user, amountDeposited); + + vm.startPrank(user); + collateralAsset.approve(address(market), type(uint).max); + market.modifyCollateral(int(amountDeposited)); + vm.stopPrank(); + + vm.prank(user); + market.modifyCollateral(-int(amountWithdrawn)); + + assertEq(market.collateral(user), amountDeposited - amountWithdrawn); + assertEq(collateralAsset.balanceOf(user), amountWithdrawn); + assertEq(collateralAsset.balanceOf(address(market)), amountDeposited - amountWithdrawn); + } + + function testTwoUsersSupply( + address firstUser, + uint firstAmount, + uint maxLLTV, + address secondUser, + uint secondAmount + ) public { + vm.assume(firstUser != secondUser); + vm.assume(firstUser != address(market)); + vm.assume(secondUser != address(market)); + vm.assume(maxLLTV < N); + firstAmount = bound(firstAmount, 1, 2 ** 64); + secondAmount = bound(secondAmount, 0, 2 ** 64); + + borrowableAsset.setBalance(firstUser, firstAmount); + vm.startPrank(firstUser); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(firstAmount), maxLLTV); + vm.stopPrank(); + + borrowableAsset.setBalance(secondUser, secondAmount); + vm.startPrank(secondUser); + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(secondAmount), maxLLTV); + vm.stopPrank(); + + assertEq(market.supplyShare(firstUser, maxLLTV), 1e18, "first user supply balance"); + assertEq( + market.supplyShare(secondUser, maxLLTV), secondAmount * 1e18 / firstAmount, "second user supply balance" + ); + } +} diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts new file mode 100644 index 000000000..09e8b58c2 --- /dev/null +++ b/test/hardhat/Market.spec.ts @@ -0,0 +1,90 @@ +import { BigNumber, Wallet, constants } from "ethers"; +import hre from "hardhat"; + +import { hexZeroPad } from "@ethersproject/bytes"; +import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; + +import { Market, OracleMock, ERC20Mock } from "types"; + +const iterations = 500; + +let seed = 42; + +function next() { + seed = seed * 16807 % 2147483647; + return seed; +} + +function random() { + return (next() - 1) / 2147483646; +} + +describe("Market", () => { + let signers: SignerWithAddress[]; + + let borrowable: ERC20Mock; + let collateral: ERC20Mock; + let borrowableOracle: OracleMock; + let collateralOracle: OracleMock; + let market: Market; + + const initBalance = constants.MaxUint256.div(2); + + beforeEach(async () => { + signers = await hre.ethers.getSigners(); + + const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", signers[0]); + + borrowable = await ERC20MockFactory.deploy("DAI", "DAI", 18); + collateral = await ERC20MockFactory.deploy("Wrapped BTC", "WBTC", 18); + + const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", signers[0]); + + borrowableOracle = await OracleMockFactory.deploy(); + collateralOracle = await OracleMockFactory.deploy(); + + const MarketFactory = await hre.ethers.getContractFactory("Market", signers[0]); + + market = await MarketFactory.deploy( + borrowable.address, + collateral.address, + borrowableOracle.address, + collateralOracle.address + ); + }); + + it("should simulate gas cost", async () => { + const n = (await market.getN()).toNumber(); + + for (let i = 1; i < iterations; ++i) { + console.log(i, "/", iterations); + + const user = new Wallet(hexZeroPad(BigNumber.from(i).toHexString(), 32), hre.ethers.provider); + await setBalance(user.address, initBalance); + await borrowable.setBalance(user.address, initBalance); + await borrowable.connect(user).approve(market.address, constants.MaxUint256); + await collateral.setBalance(user.address, initBalance); + await collateral.connect(user).approve(market.address, constants.MaxUint256); + + let amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); + const bucket = Math.floor(random() * n); + + let supplyOnly: boolean = random() < 2 / 3; + if (supplyOnly) { + await market.connect(user).modifyDeposit(amount, bucket); + await market.connect(user).modifyDeposit(amount.div(2).mul(-1), bucket); + } else { + const totalSupply = await market.totalSupply(bucket); + const totalBorrow = await market.totalBorrow(bucket); + let liq = BigNumber.from(totalSupply).sub(BigNumber.from(totalBorrow)); + amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); + + await market.connect(user).modifyCollateral(amount); + await market.connect(user).modifyBorrow(amount.div(2), bucket); + await market.connect(user).modifyBorrow(amount.div(4).mul(-1), bucket); + await market.connect(user).modifyCollateral(amount.div(8).mul(-1)); + } + } + }); +}); From cec83d891fb5a85787e7b0165b5b2513480e760d Mon Sep 17 00:00:00 2001 From: MathisGD Date: Sun, 25 Jun 2023 17:53:03 +0200 Subject: [PATCH 002/112] chore: hardhat dependencies --- .gitignore | 8 + hardhat.config.ts | 53 + package.json | 49 + tsconfig.json | 15 + yarn.lock | 5394 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 5519 insertions(+) create mode 100644 hardhat.config.ts create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index 85198aaa5..6f198470a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,11 @@ docs/ # Dotenv file .env + +# Node modules +/node_modules + +# Hardhat +/types +/cache_hardhat +/artifacts diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 000000000..fb98ad52a --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,53 @@ +import * as dotenv from "dotenv"; +import "ethers-maths"; +import "hardhat-deal"; +import "hardhat-gas-reporter"; +import "hardhat-tracer"; +import { HardhatUserConfig } from "hardhat/config"; +import "solidity-coverage"; + +import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomicfoundation/hardhat-foundry"; +import "@nomicfoundation/hardhat-network-helpers"; +import "@nomiclabs/hardhat-ethers"; +import "@typechain/hardhat"; + +dotenv.config(); + +const config: HardhatUserConfig = { + defaultNetwork: "hardhat", + networks: { + hardhat: { + chainId: 1, + gasPrice: 0, + initialBaseFeePerGas: 0, + }, + }, + solidity: { + compilers: [ + { + version: "0.8.19", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + viaIR: true, + }, + }, + ], + }, + mocha: { + timeout: 3000000, + }, + typechain: { + outDir: "types/", + externalArtifacts: ["deps/**/*.json"], + }, + tracer: { + defaultVerbosity: 1, + gasCost: true, + }, +}; + +export default config; diff --git a/package.json b/package.json new file mode 100644 index 000000000..b7becd9d0 --- /dev/null +++ b/package.json @@ -0,0 +1,49 @@ +{ + "scripts": { + "postinstall": "husky install", + "compile": "npx hardhat compile --force", + "test": "npx hardhat test" + }, + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/providers": "^5.7.2", + "ethers": "^5.7.2", + "ethers-maths": "^3.5.1", + "lodash": "^4.17.21", + "openzeppelin-contracts": "^4.0.0" + }, + "devDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", + "@nomicfoundation/hardhat-foundry": "^1.0.1", + "@nomicfoundation/hardhat-network-helpers": "^1.0.8", + "@nomiclabs/hardhat-ethers": "^2.2.3", + "@trivago/prettier-plugin-sort-imports": "^4.1.1", + "@typechain/ethers-v5": "^10.2.1", + "@typechain/hardhat": "^6.1.6", + "@types/chai": "^4.3.5", + "@types/lodash": "^4.14.195", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.5", + "chai": "^4.3.7", + "dotenv": "^16.1.3", + "hardhat": "^2.14.1", + "hardhat-deal": "^1.3.0", + "hardhat-gas-reporter": "^1.0.9", + "hardhat-tracer": "2.3.2", + "husky": "8.0.3", + "lint-staged": "13.2.2", + "prettier": "^2.8.8", + "solidity-coverage": "^0.8.2", + "ts-node": "^10.9.1", + "typechain": "^8.2.0", + "typescript": "5.0.4" + }, + "lint-staged": { + "*.sol": "forge fmt", + "*.js": "yarn prettier", + "*.ts": "yarn prettier", + "*.json": "yarn prettier", + "*.yml": "yarn prettier" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..b301bff05 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "esnext", + "strict": true, + "esModuleInterop": true, + "rootDir": ".", + "baseUrl": ".", + "outDir": "dist", + "moduleResolution": "nodenext", + "resolveJsonModule": true, + "declaration": true + }, + "include": ["types", "test/hardhat"], + "files": ["./hardhat.config.ts"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..88f75030b --- /dev/null +++ b/yarn.lock @@ -0,0 +1,5394 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/generator@7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" + integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/generator@^7.17.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.16.7": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.16.7": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" + integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.17.3", "@babel/parser@^7.20.5", "@babel/parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" + integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== + +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@babel/types@^7.17.0", "@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + +"@chainsafe/as-sha256@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" + integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg== + +"@chainsafe/persistent-merkle-tree@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz#4c9ee80cc57cd3be7208d98c40014ad38f36f7ff" + integrity sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + +"@chainsafe/persistent-merkle-tree@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz#2b4a62c9489a5739dedd197250d8d2f5427e9f63" + integrity sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + +"@chainsafe/ssz@^0.10.0": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.10.2.tgz#c782929e1bb25fec66ba72e75934b31fd087579e" + integrity sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + "@chainsafe/persistent-merkle-tree" "^0.5.0" + +"@chainsafe/ssz@^0.9.2": + version "0.9.4" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.9.4.tgz#696a8db46d6975b600f8309ad3a12f7c0e310497" + integrity sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + "@chainsafe/persistent-merkle-tree" "^0.4.2" + case "^1.6.3" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" + integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" + integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" + integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/units@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/wallet@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" + integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/json-wallets" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" + integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + +"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@nomicfoundation/ethereumjs-block@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz#6f89664f55febbd723195b6d0974773d29ee133d" + integrity sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + ethereum-cryptography "0.1.3" + ethers "^5.7.1" + +"@nomicfoundation/ethereumjs-blockchain@7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.1.tgz#80e0bd3535bfeb9baa29836b6f25123dab06a726" + integrity sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A== + dependencies: + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-ethash" "3.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + abstract-level "^1.0.3" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + level "^8.0.0" + lru-cache "^5.1.1" + memory-level "^1.0.0" + +"@nomicfoundation/ethereumjs-common@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.1.tgz#4702d82df35b07b5407583b54a45bf728e46a2f0" + integrity sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g== + dependencies: + "@nomicfoundation/ethereumjs-util" "9.0.1" + crc-32 "^1.2.0" + +"@nomicfoundation/ethereumjs-ethash@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.1.tgz#65ca494d53e71e8415c9a49ef48bc921c538fc41" + integrity sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w== + dependencies: + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + abstract-level "^1.0.3" + bigint-crypto-utils "^3.0.23" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-evm@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.1.tgz#f35681e203363f69ce2b3d3bf9f44d4e883ca1f1" + integrity sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ== + dependencies: + "@ethersproject/providers" "^5.7.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + mcl-wasm "^0.7.1" + rustbn.js "~0.2.0" + +"@nomicfoundation/ethereumjs-rlp@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.1.tgz#0b30c1cf77d125d390408e391c4bb5291ef43c28" + integrity sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ== + +"@nomicfoundation/ethereumjs-statemanager@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.1.tgz#8824a97938db4471911e2d2f140f79195def5935" + integrity sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + ethers "^5.7.1" + js-sdsl "^4.1.4" + +"@nomicfoundation/ethereumjs-trie@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.1.tgz#662c55f6b50659fd4b22ea9f806a7401cafb7717" + integrity sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + "@types/readable-stream" "^2.3.13" + ethereum-cryptography "0.1.3" + readable-stream "^3.6.0" + +"@nomicfoundation/ethereumjs-tx@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.1.tgz#7629dc2036b4a33c34e9f0a592b43227ef4f0c7d" + integrity sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w== + dependencies: + "@chainsafe/ssz" "^0.9.2" + "@ethersproject/providers" "^5.7.2" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-util@9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.1.tgz#530cda8bae33f8b5020a8f199ed1d0a2ce48ec89" + integrity sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA== + dependencies: + "@chainsafe/ssz" "^0.10.0" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-vm@7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.1.tgz#7d035e0993bcad10716c8b36e61dfb87fa3ca05f" + integrity sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ== + dependencies: + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-blockchain" "7.0.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-evm" "2.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-statemanager" "2.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + debug "^4.3.3" + ethereum-cryptography "0.1.3" + mcl-wasm "^0.7.1" + rustbn.js "~0.2.0" + +"@nomicfoundation/hardhat-chai-matchers@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.6.tgz#72a2e312e1504ee5dd73fe302932736432ba96bc" + integrity sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@types/chai-as-promised" "^7.1.3" + chai-as-promised "^7.1.1" + deep-eql "^4.0.1" + ordinal "^1.0.3" + +"@nomicfoundation/hardhat-foundry@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.0.1.tgz#22b11a78a179ec7ed6840ee8c10add888fd23dc8" + integrity sha512-sQEaX3rik6Gclmek4MqCyOc9+vOM0ZS40eUARrB6K9t6wthqCwJ29CClfxLdbrjn/3MM5hX8ioD+/9OqdGVxSQ== + dependencies: + chalk "^2.4.2" + +"@nomicfoundation/hardhat-network-helpers@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.8.tgz#e4fe1be93e8a65508c46d73c41fa26c7e9f84931" + integrity sha512-MNqQbzUJZnCMIYvlniC3U+kcavz/PhhQSsY90tbEtUyMj/IQqsLwIRZa4ctjABh3Bz0KCh9OXUZ7Yk/d9hr45Q== + dependencies: + ethereumjs-util "^7.1.4" + +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz#4c858096b1c17fe58a474fe81b46815f93645c15" + integrity sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz#6e25ccdf6e2d22389c35553b64fe6f3fdaec432c" + integrity sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA== + +"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz#0a224ea50317139caeebcdedd435c28a039d169c" + integrity sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz#dfa085d9ffab9efb2e7b383aed3f557f7687ac2b" + integrity sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz#c9e06b5d513dd3ab02a7ac069c160051675889a4" + integrity sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz#8d328d16839e52571f72f2998c81e46bf320f893" + integrity sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz#9b49d0634b5976bb5ed1604a1e1b736f390959bb" + integrity sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w== + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz#e2867af7264ebbcc3131ef837878955dd6a3676f" + integrity sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg== + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz#0685f78608dd516c8cdfb4896ed451317e559585" + integrity sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz#c9a44f7108646f083b82e851486e0f6aeb785836" + integrity sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz#f5f4d36d3f66752f59a57e7208cd856f3ddf6f2d" + integrity sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.1" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" + +"@nomiclabs/hardhat-ethers@^2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz#b41053e360c31a32c2640c9a45ee981a7e603fe0" + integrity sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg== + +"@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" + integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== + dependencies: + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" + +"@sentry/core@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" + integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/hub@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" + integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== + dependencies: + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/minimal@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" + integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" + integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== + dependencies: + "@sentry/core" "5.30.0" + "@sentry/hub" "5.30.0" + "@sentry/tracing" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" + integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/types@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" + integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== + +"@sentry/utils@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" + integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== + dependencies: + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": + version "0.14.5" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" + integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@trivago/prettier-plugin-sort-imports@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.1.1.tgz#71c3c1ae770c3738b6fc85710714844477574ffd" + integrity sha512-dQ2r2uzNr1x6pJsuh/8x0IRA3CBUB+pWEW3J/7N98axqt7SQSm+2fy0FLNXvXGg77xEDC7KHxJlHfLYyi7PDcw== + dependencies: + "@babel/generator" "7.17.7" + "@babel/parser" "^7.20.5" + "@babel/traverse" "7.17.3" + "@babel/types" "7.17.0" + javascript-natural-sort "0.7.1" + lodash "^4.17.21" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@typechain/ethers-v5@^10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz#50241e6957683281ecfa03fb5a6724d8a3ce2391" + integrity sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A== + dependencies: + lodash "^4.17.15" + ts-essentials "^7.0.1" + +"@typechain/hardhat@^6.1.6": + version "6.1.6" + resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.6.tgz#1a749eb35e5054c80df531cf440819cb347c62ea" + integrity sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA== + dependencies: + fs-extra "^9.1.0" + +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + dependencies: + "@types/node" "*" + +"@types/chai-as-promised@^7.1.3": + version "7.1.5" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz#6e016811f6c7a64f2eed823191c3a6955094e255" + integrity sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== + +"@types/concat-stream@^1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74" + integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA== + dependencies: + "@types/node" "*" + +"@types/form-data@0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw== + dependencies: + "@types/node" "*" + +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/lodash@^4.14.195": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + +"@types/lru-cache@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/mocha@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" + integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== + +"@types/node@*", "@types/node@^20.2.5": + version "20.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" + integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + +"@types/node@^10.0.3": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + +"@types/node@^8.0.0": + version "8.10.66" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" + integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/prettier@^2.1.1": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + +"@types/qs@^6.2.31": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/readable-stream@^2.3.13": + version "2.3.15" + resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" + integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== + dependencies: + "@types/node" "*" + safe-buffer "~5.1.1" + +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" + integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== + dependencies: + buffer "^6.0.3" + catering "^2.1.0" + is-buffer "^2.0.5" + level-supports "^4.0.0" + level-transcoder "^1.0.1" + module-error "^1.0.1" + queue-microtask "^1.2.3" + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== + +address@^1.0.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +anymatch@~3.1.1, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1, array-back@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" + integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async@1.x: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bigint-crypto-utils@^3.0.23: + version "3.2.2" + resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.2.2.tgz#e30a49ec38357c6981cd3da5aaa6480b1f752ee4" + integrity sha512-U1RbE3aX9ayCUVcIPHuPDPKcK3SFOXf93J1UK/iHlJuQB7bhagPIX06/CLpLEsDThJ7KA4Dhrnzynl+d2weTiw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + +bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browser-level@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" + integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.1" + module-error "^1.0.2" + run-parallel-limit "^1.1.0" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +case@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== + +caseless@^0.12.0, caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +catering@^2.1.0, catering@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" + integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== + +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + +chalk@^2.0.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +"charenc@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +chokidar@3.5.3, chokidar@^3.4.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +classic-level@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.3.0.tgz#5e36680e01dc6b271775c093f2150844c5edd5c8" + integrity sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg== + dependencies: + abstract-level "^1.0.2" + catering "^2.1.0" + module-error "^1.0.1" + napi-macros "^2.2.2" + node-gyp-build "^4.3.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-table3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.19: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +colors@1.4.0, colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +command-line-args@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-usage@^6.1.0: + version "6.1.3" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== + dependencies: + array-back "^4.0.2" + chalk "^2.4.2" + table-layout "^1.0.2" + typical "^5.2.0" + +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@^1.6.0, concat-stream@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +"crypt@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +death@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" + integrity sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w== + +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-eql@^4.0.1, deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.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== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +detect-port@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" + integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ== + dependencies: + address "^1.0.1" + debug "4" + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== + dependencies: + heap ">= 0.2.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dotenv@^16.1.3: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +enquirer@^2.3.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== + dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.0" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@1.0.5, 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== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A== + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +esprima@2.7.x, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eth-gas-reporter@^0.2.25: + version "0.2.25" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz#546dfa946c1acee93cb1a94c2a1162292d6ff566" + integrity sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ== + dependencies: + "@ethersproject/abi" "^5.0.0-beta.146" + "@solidity-parser/parser" "^0.14.0" + cli-table3 "^0.5.0" + colors "1.4.0" + ethereum-cryptography "^1.0.3" + ethers "^4.0.40" + fs-readdir-recursive "^1.1.0" + lodash "^4.17.14" + markdown-table "^1.1.3" + mocha "^7.1.1" + req-cwd "^2.0.0" + request "^2.88.0" + request-promise-native "^1.0.5" + sha1 "^1.1.1" + sync-request "^6.0.0" + +ethereum-bloom-filters@^1.0.6: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" + integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== + dependencies: + js-sha3 "^0.8.0" + +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-cryptography@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" + integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/bip32" "1.1.5" + "@scure/bip39" "1.1.1" + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethers-maths@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/ethers-maths/-/ethers-maths-3.5.1.tgz#a556e98d2ee9a6558076ceee0eb35623a713bab1" + integrity sha512-Px7Gc+IxZ3uh260Gg5A0csLqfwZp/YFrLSReeQIe2Tl42BI9fuFlOuBzWkNovniF3jqTwwSLeuo4l2+OWnz+FQ== + dependencies: + ethers "^5.0.0" + +ethers@^4.0.40: + version "4.0.49" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" + integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg== + dependencies: + aes-js "3.0.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethers@^5.0.0, ethers@^5.6.1, ethers@^5.7.1, ethers@^5.7.2: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.0.3: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== + dependencies: + locate-path "^2.0.0" + +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== + dependencies: + is-buffer "~2.0.3" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +follow-redirects@^1.12.1: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +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" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data@^2.2.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^7.0.0, fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +functions-have-names@^1.2.2, functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-port@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== + +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +ghost-testrpc@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92" + integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ== + dependencies: + chalk "^2.4.2" + node-emoji "^1.10.0" + +glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA== + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.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, graceful-fs@^4.1.9, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +handlebars@^4.0.1: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hardhat-deal@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/hardhat-deal/-/hardhat-deal-1.4.0.tgz#3f42f52603c1f9eeb7e583ca7261478d17b875a4" + integrity sha512-8+p0c7u3RsRWf5mzAAUbJTq11HAJJb+tj6AcM9w4ObN1LTMBouQ7D6sc3zQwLP8dddYbA7z0wrkp+zn61cPxCg== + +hardhat-gas-reporter@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz#9a2afb354bc3b6346aab55b1c02ca556d0e16450" + integrity sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg== + dependencies: + array-uniq "1.0.3" + eth-gas-reporter "^0.2.25" + sha1 "^1.1.1" + +hardhat-tracer@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/hardhat-tracer/-/hardhat-tracer-2.3.2.tgz#c972a82974d4db0fbe99d9b809b9c462a79d36a6" + integrity sha512-Sw//tVU/Qyng5rKULONIuYpr4yEobjfh5seIaRT8gYXKDo22eUllTMlJEV2ejsZ92X2x+PSWy1/H27aejV31/w== + dependencies: + chalk "^4.1.2" + ethers "^5.6.1" + +hardhat@^2.14.1: + version "2.16.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.16.0.tgz#c5611d433416b31f6ce92f733b1f1b5236ad6230" + integrity sha512-7VQEJPQRAZdtrYUZaU9GgCpP3MBNy/pTdscARNJQMWKj5C+R7V32G5uIZKIqZ4QiqXa6CBfxxe+G+ahxUbHZHA== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/ethereumjs-block" "5.0.1" + "@nomicfoundation/ethereumjs-blockchain" "7.0.1" + "@nomicfoundation/ethereumjs-common" "4.0.1" + "@nomicfoundation/ethereumjs-evm" "2.0.1" + "@nomicfoundation/ethereumjs-rlp" "5.0.1" + "@nomicfoundation/ethereumjs-statemanager" "2.0.1" + "@nomicfoundation/ethereumjs-trie" "6.0.1" + "@nomicfoundation/ethereumjs-tx" "5.0.1" + "@nomicfoundation/ethereumjs-util" "9.0.1" + "@nomicfoundation/ethereumjs-vm" "7.0.1" + "@nomicfoundation/solidity-analyzer" "^0.1.0" + "@sentry/node" "^5.18.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^1.0.3" + ethereumjs-abi "^0.6.8" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "7.2.0" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + keccak "^3.0.2" + lodash "^4.17.11" + mnemonist "^0.38.0" + mocha "^10.0.0" + p-map "^4.0.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + tsort "0.0.1" + undici "^5.14.0" + uuid "^8.3.2" + ws "^7.4.6" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "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.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.0, has-symbols@^1.0.2, 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: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +"heap@>= 0.2.0": + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +http-basic@^8.1.1: + version "8.1.3" + resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" + integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== + dependencies: + caseless "^0.12.0" + concat-stream "^1.6.2" + http-response-object "^3.0.1" + parse-cache-control "^1.0.1" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-response-object@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" + integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== + dependencies: + "@types/node" "^10.0.3" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +husky@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" + integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.1.1: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +immutable@^4.0.0-rc.12: + version "4.3.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" + integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^2.0.5, is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + 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-core-module@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +javascript-natural-sort@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" + integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== + +js-sdsl@^4.1.4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.1.tgz#9e3c7b566d8d9a7e1fe8fc26d00b5ab0f8918ab3" + integrity sha512-6Gsx8R0RucyePbWqPssR8DyfuXmLBooYN5cZFZKjHGnQuaf7pEzhtpceagJxVu4LqhYY5EYA7nko3FmeHZ1KbA== + +js-sha3@0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== + +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" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@3.x: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonschema@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +keccak@^3.0.0, keccak@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" + integrity sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== + optionalDependencies: + graceful-fs "^4.1.9" + +level-supports@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" + integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== + +level-transcoder@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" + integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== + dependencies: + buffer "^6.0.3" + module-error "^1.0.1" + +level@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394" + integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ== + dependencies: + browser-level "^1.0.1" + classic-level "^1.2.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lint-staged@13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.2.tgz#5e711d3139c234f73402177be2f8dd312e6508ca" + integrity sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA== + dependencies: + chalk "5.2.0" + cli-truncate "^3.1.0" + commander "^10.0.0" + debug "^4.3.4" + execa "^7.0.0" + lilconfig "2.1.0" + listr2 "^5.0.7" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-inspect "^1.12.3" + pidtree "^0.6.0" + string-argv "^0.3.1" + yaml "^2.2.2" + +listr2@^5.0.7: + version "5.0.8" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23" + integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.19" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.8.0" + through "^2.3.8" + wrap-ansi "^7.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + +loupe@^2.3.1: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +markdown-table@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" + integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== + +mcl-wasm@^0.7.1: + version "0.7.9" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" + integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memory-level@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" + integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== + dependencies: + abstract-level "^1.0.0" + functional-red-black-tree "^1.0.1" + module-error "^1.0.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3, merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@0.5.x: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mnemonist@^0.38.0: + version "0.38.5" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" + integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== + dependencies: + obliterator "^2.0.0" + +mocha@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6" + integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mocha@^10.0.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +mocha@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +module-error@^1.0.1, module-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" + integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +napi-macros@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.2.2.tgz#817fef20c3e0e40a963fbf7b37d1600bd0201044" + integrity sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g== + +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + +nopt@3.x: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg== + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-keys@^1.0.11, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.0.3: + version "2.1.6" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz#5e5c384dd209fa4efffead39e3a0512770ccc312" + integrity sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ== + dependencies: + array.prototype.reduce "^1.0.5" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.21.2" + safe-array-concat "^1.0.0" + +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + +once@1.x, once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +openzeppelin-contracts@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/openzeppelin-contracts/-/openzeppelin-contracts-4.0.0.tgz#3b56ae3c5f6a59eaca2eacf935e3ebc18a59cd78" + integrity sha512-SlBeiWSJKhX9EY2Nlgev4P/LMBWWSk6wL+eqikAe9gODpxrQwLOx4Rw3vyHuJaChrUTlGyR4Gl1NqJMw0J15eQ== + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ordinal@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d" + integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-cache-control@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.6, path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +prettier@^2.3.1, prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== + dependencies: + asap "~2.0.6" + +psl@^1.1.28: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +qs@^6.4.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +queue-microtask@^1.2.2, queue-microtask@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +raw-body@^2.4.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.2.2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + +regexp.prototype.flags@^1.4.3: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + +req-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" + integrity sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ== + dependencies: + req-from "^2.0.0" + +req-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-from/-/req-from-2.0.0.tgz#d74188e47f93796f4aa71df6ee35ae689f3e0e70" + integrity sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA== + dependencies: + resolve-from "^3.0.0" + +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.5: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== + +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== + +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +resolve@^1.1.6: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.3, rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +run-parallel-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" + integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== + dependencies: + queue-microtask "^1.2.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^7.8.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sc-istanbul@^0.4.5: + version "0.4.6" + resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" + integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@3.0.1, scrypt-js@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semver@^5.5.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.4: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog== + +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== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sha1@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" + integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA== + dependencies: + charenc ">= 0.0.1" + crypt ">= 0.0.1" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shelljs@^0.8.3: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solidity-coverage@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.3.tgz#72ce51e5ca9ea1182bbf6085eb1cf526f0603b52" + integrity sha512-hbcNgj5z8zzgTlnp4F0pXiqj1v5ua8P4DH5i9cWOBtFPfUuIohLoXu5WiAixexWmpKVjyxXqupnu/mPb4IGr7Q== + dependencies: + "@ethersproject/abi" "^5.0.9" + "@solidity-parser/parser" "^0.14.1" + chalk "^2.4.2" + death "^1.1.0" + detect-port "^1.3.0" + difflib "^0.2.4" + fs-extra "^8.1.0" + ghost-testrpc "^0.0.2" + global-modules "^2.0.0" + globby "^10.0.1" + jsonschema "^1.2.4" + lodash "^4.17.15" + mocha "7.1.2" + node-emoji "^1.10.0" + pify "^4.0.1" + recursive-readdir "^2.2.2" + sc-istanbul "^0.4.5" + semver "^7.3.4" + shelljs "^0.8.3" + web3-utils "^1.3.6" + +source-map-support@^0.5.13: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA== + dependencies: + amdefine ">=0.0.4" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +sshpk@^1.7.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +string-argv@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-format@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" + integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== + +"string-width@^1.0.2 || 2", string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^3.1.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A== + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +sync-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" + integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== + dependencies: + http-response-object "^3.0.1" + sync-rpc "^1.2.1" + then-request "^6.0.0" + +sync-rpc@^1.2.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" + integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== + dependencies: + get-port "^3.1.0" + +table-layout@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + +then-request@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" + integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== + dependencies: + "@types/concat-stream" "^1.6.0" + "@types/form-data" "0.0.33" + "@types/node" "^8.0.0" + "@types/qs" "^6.2.31" + caseless "~0.12.0" + concat-stream "^1.6.0" + form-data "^2.2.0" + http-basic "^8.1.1" + http-response-object "^3.0.1" + promise "^8.0.0" + qs "^6.4.0" + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@^2.3.3, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +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" + integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== + dependencies: + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-usage "^6.1.0" + string-format "^2.0.0" + +ts-essentials@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" + integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.1.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +typechain@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.2.0.tgz#bd4fc8f111d4405e36858bae6f744604617b60f3" + integrity sha512-tZqhqjxJ9xAS/Lh32jccTjMkpx7sTdUVVHAy5Bf0TIer5QFNYXotiX74oCvoVYjyxUKDK3MXHtMFzMyD3kE+jg== + dependencies: + "@types/prettier" "^2.1.1" + debug "^4.3.1" + fs-extra "^7.0.0" + glob "7.1.7" + js-sha3 "^0.8.0" + lodash "^4.17.15" + mkdirp "^1.0.4" + prettier "^2.3.1" + ts-command-line-args "^2.2.0" + ts-essentials "^7.0.1" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undici@^5.14.0: + version "5.22.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b" + integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw== + dependencies: + busboy "^1.6.0" + +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== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +web3-utils@^1.3.6: + version "1.10.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.0.tgz#ca4c1b431a765c14ac7f773e92e0fd9377ccf578" + integrity sha512-kSaCM0uMcZTNUSmn5vMEhlo02RObGNRRCkdX0V9UTAU0+lrvn0HSaudyCo6CQzuXUsnuY2ERJGCGPfeWmv19Rg== + dependencies: + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereumjs-util "^7.1.0" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + +which@1.3.1, which@^1.1.1, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From eeaa50bdbd198cc22993d0f7ca0693f467e62948 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 26 Jun 2023 16:21:49 +0200 Subject: [PATCH 003/112] feat: independent collateral --- src/Market.sol | 36 +++++++++++++++++------------------- test/forge/Market.t.sol | 22 +++++++++++++--------- test/hardhat/Market.spec.ts | 4 ++-- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index fefe23c22..725f6751f 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -40,7 +40,7 @@ contract Market { // User' borrow balances. mapping(address => mapping(uint => uint)) public borrowShare; // User' collateral balance. - mapping(address => uint) public collateral; + mapping(address => mapping(uint => uint)) public collateral; // Market total supply. mapping(uint => uint) public totalSupply; // Market total supply shares. @@ -114,7 +114,7 @@ contract Market { totalBorrow[bucket] = uint(int(totalBorrow[bucket]) + amount); if (amount > 0) { - checkHealth(msg.sender); + checkHealth(msg.sender, bucket); require(totalBorrow[bucket] <= totalSupply[bucket], "not enough liquidity"); } @@ -122,10 +122,15 @@ contract Market { } /// @dev positive amount to deposit. - function modifyCollateral(int amount) external { - collateral[msg.sender] = (int(collateral[msg.sender]) + amount).safeToUint(); + function modifyCollateral(int amount, uint bucket) external { + if (amount == 0) return; + require(bucket < N, "unknown bucket"); + + accrueInterests(bucket); + + collateral[msg.sender][bucket] = (int(collateral[msg.sender][bucket]) + amount).safeToUint(); - if (amount < 0) checkHealth(msg.sender); + if (amount < 0) checkHealth(msg.sender, bucket); IERC20(collateralAsset).handleTransfer({user: msg.sender, amountIn: amount}); } @@ -147,20 +152,13 @@ contract Market { // Health check. - function checkHealth(address user) public view { - // Temporary trick to ease testing. - if (IOracle(borrowableOracle).price() == 0) return; - uint collateralValueRequired; - for (uint bucket = 1; bucket < N; bucket++) { - if (totalBorrowShares[bucket] > 0 && borrowShare[user][bucket] > 0) { - uint borrowAtBucket = - borrowShare[user][bucket].wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]); - collateralValueRequired += - borrowAtBucket.wMul(IOracle(borrowableOracle).price()).wDiv(bucketToLLTV(bucket)); - } + function checkHealth(address user, uint bucket) public view { + if (totalBorrowShares[bucket] > 0 && borrowShare[user][bucket] > 0) { + uint borrowValue = borrowShare[user][bucket].wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]).wMul( + IOracle(borrowableOracle).price() + ); + uint collateralValue = collateral[user][bucket].wMul(IOracle(collateralOracle).price()); + require(collateralValue.wMul(bucketToLLTV(bucket)) >= borrowValue, "not enough collateral"); } - require( - collateral[user].wMul(IOracle(collateralOracle).price()) >= collateralValueRequired, "not enough collateral" - ); } } diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 63f4c18cf..25383f4ba 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -187,12 +187,12 @@ contract MarketTest is Test { vm.startPrank(borrower); collateralAsset.approve(address(market), type(uint).max); - market.modifyCollateral(int(amountCollateral)); + market.modifyCollateral(int(amountCollateral), 1); vm.stopPrank(); uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); - if (borrowValue == 0 || (collateralValue > 0 && borrowValue.wDiv(bucketToLLTV(1)) <= collateralValue)) { + if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(bucketToLLTV(1)))) { vm.prank(borrower); market.modifyBorrow(int(amountBorrowed), 1); } else { @@ -244,22 +244,26 @@ contract MarketTest is Test { ); } - function testDepositCollateral(address user, uint amount) public { + function testDepositCollateral(address user, uint amount, uint lLTV) public { + vm.assume(lLTV < N); + collateralAsset.setBalance(user, amount); vm.assume(int(amount) >= 0); vm.assume(user != address(market)); vm.startPrank(user); collateralAsset.approve(address(market), type(uint).max); - market.modifyCollateral(int(amount)); + market.modifyCollateral(int(amount), lLTV); vm.stopPrank(); - assertEq(market.collateral(user), amount); + assertEq(market.collateral(user, lLTV), amount); assertEq(collateralAsset.balanceOf(user), 0); assertEq(collateralAsset.balanceOf(address(market)), amount); } - function testWithdrawCollateral(address user, uint amountDeposited, uint amountWithdrawn) public { + function testWithdrawCollateral(address user, uint amountDeposited, uint amountWithdrawn, uint lLTV) public { + vm.assume(lLTV < N); + vm.assume(amountDeposited >= amountWithdrawn); vm.assume(int(amountDeposited) >= 0); vm.assume(user != address(market)); @@ -268,13 +272,13 @@ contract MarketTest is Test { vm.startPrank(user); collateralAsset.approve(address(market), type(uint).max); - market.modifyCollateral(int(amountDeposited)); + market.modifyCollateral(int(amountDeposited), lLTV); vm.stopPrank(); vm.prank(user); - market.modifyCollateral(-int(amountWithdrawn)); + market.modifyCollateral(-int(amountWithdrawn), lLTV); - assertEq(market.collateral(user), amountDeposited - amountWithdrawn); + assertEq(market.collateral(user, lLTV), amountDeposited - amountWithdrawn); assertEq(collateralAsset.balanceOf(user), amountWithdrawn); assertEq(collateralAsset.balanceOf(address(market)), amountDeposited - amountWithdrawn); } diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 09e8b58c2..3077e126f 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -80,10 +80,10 @@ describe("Market", () => { let liq = BigNumber.from(totalSupply).sub(BigNumber.from(totalBorrow)); amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); - await market.connect(user).modifyCollateral(amount); + await market.connect(user).modifyCollateral(amount, bucket); await market.connect(user).modifyBorrow(amount.div(2), bucket); await market.connect(user).modifyBorrow(amount.div(4).mul(-1), bucket); - await market.connect(user).modifyCollateral(amount.div(8).mul(-1)); + await market.connect(user).modifyCollateral(amount.div(8).mul(-1), bucket); } } }); From f66cc1b0840fdfb0aab24e9e7a561ec3e16e9202 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 26 Jun 2023 17:41:42 +0200 Subject: [PATCH 004/112] test: improve testing --- test/forge/Market.t.sol | 264 +++++++++++++++------------------------- 1 file changed, 100 insertions(+), 164 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 25383f4ba..af1308793 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -11,6 +11,8 @@ import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; contract MarketTest is Test { using MathLib for uint; + address private constant borrower = address(1234); + Market private market; ERC20 private borrowableAsset; ERC20 private collateralAsset; @@ -29,8 +31,17 @@ contract MarketTest is Test { address(collateralOracle) ); + // We set the price of the borrowable asset to zero so that borrowers + // don't need to deposit any collateral. borrowableOracle.setPrice(0); collateralOracle.setPrice(1e18); + + borrowableAsset.approve(address(market), type(uint).max); + collateralAsset.approve(address(market), type(uint).max); + vm.startPrank(borrower); + borrowableAsset.approve(address(market), type(uint).max); + collateralAsset.approve(address(market), type(uint).max); + vm.stopPrank(); } function invariantParams() public { @@ -54,264 +65,189 @@ contract MarketTest is Test { assertGe(borrowableAsset.balanceOf(address(market)), expectedMinimumBalance); } - function testDeposit(address user, uint amount, uint maxLLTV) public { + function testDeposit(uint amount, uint bucket) public { amount = bound(amount, 1, 2 ** 64); - vm.assume(user != address(market)); - vm.assume(maxLLTV < N); - - borrowableAsset.setBalance(user, amount); + vm.assume(bucket < N); - vm.startPrank(user); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(amount), maxLLTV); - vm.stopPrank(); + borrowableAsset.setBalance(address(this), amount); + market.modifyDeposit(int(amount), bucket); - assertEq(market.supplyShare(user, maxLLTV), 1e18, "supply balance user"); - assertEq(borrowableAsset.balanceOf(user), 0, "token balance user"); - assertEq(borrowableAsset.balanceOf(address(market)), amount, "token balance market"); + assertEq(market.supplyShare(address(this), bucket), 1e18); + assertEq(borrowableAsset.balanceOf(address(this)), 0); + assertEq(borrowableAsset.balanceOf(address(market)), amount); } - function testBorrow(address lender, uint maxLLTV, uint amountLent, address borrower, uint amountBorrowed) public { + function testBorrow(uint amountLent, uint amountBorrowed, uint bucket) public { amountLent = bound(amountLent, 0, 2 ** 64); amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); - vm.assume(lender != address(market)); - vm.assume(maxLLTV < N); + vm.assume(bucket < N); - borrowableAsset.setBalance(lender, amountLent); - - vm.startPrank(lender); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(amountLent), maxLLTV); - vm.stopPrank(); + borrowableAsset.setBalance(address(this), amountLent); + market.modifyDeposit(int(amountLent), bucket); if (amountBorrowed == 0) { - // No need to deposit any collateral as the price of the borrowed asset is 0. - market.modifyBorrow(int(amountBorrowed), maxLLTV); + market.modifyBorrow(int(amountBorrowed), bucket); return; } if (amountBorrowed > amountLent) { vm.prank(borrower); vm.expectRevert("not enough liquidity"); - // No need to deposit any collateral as the price of the borrowed asset is 0. - market.modifyBorrow(int(amountBorrowed), maxLLTV); + market.modifyBorrow(int(amountBorrowed), bucket); return; } vm.prank(borrower); - // No need to deposit any collateral as the price of the borrowed asset is 0. - market.modifyBorrow(int(amountBorrowed), maxLLTV); + market.modifyBorrow(int(amountBorrowed), bucket); - assertEq(market.borrowShare(borrower, maxLLTV), 1e18, "borrow balance borrower"); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed, "token balance borrower"); - assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed, "token balance market"); + assertEq(market.borrowShare(borrower, bucket), 1e18); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed); + assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed); } - function testWithdraw( - address lender, - uint maxLLTV, - uint amountLent, - uint amountWithdrawn, - address borrower, - uint amountBorrowed - ) public { + function testWithdraw(uint amountLent, uint amountWithdrawn, uint amountBorrowed, uint bucket) public { amountLent = bound(amountLent, 1, 2 ** 64); - vm.assume(lender != address(market)); - vm.assume(lender != borrower); - vm.assume(maxLLTV < N); + vm.assume(bucket < N); vm.assume(amountLent >= amountBorrowed); vm.assume(int(amountWithdrawn) >= 0); - borrowableAsset.setBalance(lender, amountLent); - - vm.startPrank(lender); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(amountLent), maxLLTV); - vm.stopPrank(); + borrowableAsset.setBalance(address(this), amountLent); + market.modifyDeposit(int(amountLent), bucket); - vm.startPrank(borrower); - market.modifyBorrow(int(amountBorrowed), maxLLTV); - vm.stopPrank(); + vm.prank(borrower); + market.modifyBorrow(int(amountBorrowed), bucket); // Should revert because not enough liquidity. if (amountWithdrawn > amountLent - amountBorrowed) { - vm.prank(lender); vm.expectRevert(); - market.modifyDeposit(-int(amountWithdrawn), maxLLTV); + market.modifyDeposit(-int(amountWithdrawn), bucket); return; } - vm.prank(lender); - market.modifyDeposit(-int(amountWithdrawn), maxLLTV); + market.modifyDeposit(-int(amountWithdrawn), bucket); assertApproxEqAbs( - market.supplyShare(lender, maxLLTV), - (amountLent - amountWithdrawn) * 1e18 / amountLent, - 1e3, - "supply balance lender" - ); - assertEq(borrowableAsset.balanceOf(lender), amountWithdrawn, "token balance lender"); - assertEq( - borrowableAsset.balanceOf(address(market)), - amountLent - amountBorrowed - amountWithdrawn, - "token balance market" + market.supplyShare(address(this), bucket), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3 ); + assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn); + assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed - amountWithdrawn); } function testCollateralRequirements( - address lender, - address borrower, uint amountCollateral, uint amountBorrowed, uint priceCollateral, - uint priceBorrowable + uint priceBorrowable, + uint bucket ) public { - vm.assume(lender != address(market)); - vm.assume(borrower != address(market)); - vm.assume(lender != borrower); amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); - priceBorrowable = bound(priceBorrowable, 1, 2 ** 64); + priceBorrowable = bound(priceBorrowable, 0, 2 ** 64); amountCollateral = bound(amountCollateral, 0, 2 ** 64); - priceCollateral = bound(priceCollateral, 1, 2 ** 64); + priceCollateral = bound(priceCollateral, 0, 2 ** 64); + vm.assume(bucket < N); borrowableOracle.setPrice(priceBorrowable); collateralOracle.setPrice(priceCollateral); - borrowableAsset.setBalance(lender, amountBorrowed); + borrowableAsset.setBalance(address(this), amountBorrowed); collateralAsset.setBalance(borrower, amountCollateral); - vm.startPrank(lender); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(amountBorrowed), 1); - vm.stopPrank(); + market.modifyDeposit(int(amountBorrowed), bucket); - vm.startPrank(borrower); - collateralAsset.approve(address(market), type(uint).max); - market.modifyCollateral(int(amountCollateral), 1); - vm.stopPrank(); + vm.prank(borrower); + market.modifyCollateral(int(amountCollateral), bucket); uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); - if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(bucketToLLTV(1)))) { + if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(bucketToLLTV(bucket)))) { vm.prank(borrower); - market.modifyBorrow(int(amountBorrowed), 1); + market.modifyBorrow(int(amountBorrowed), bucket); } else { vm.prank(borrower); vm.expectRevert("not enough collateral"); - market.modifyBorrow(int(amountBorrowed), 1); + market.modifyBorrow(int(amountBorrowed), bucket); } } - function testRepay( - address lender, - uint maxLLTV, - uint amountLent, - address borrower, - uint amountBorrowed, - uint amountRepaid - ) public { + function testRepay(uint amountLent, uint amountBorrowed, uint amountRepaid, uint bucket) public { amountLent = bound(amountLent, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, amountLent); amountRepaid = bound(amountRepaid, 0, amountBorrowed); - vm.assume(lender != address(market)); - vm.assume(lender != borrower); - vm.assume(maxLLTV < N); + vm.assume(bucket < N); - borrowableAsset.setBalance(lender, amountLent); - - vm.startPrank(lender); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(amountLent), maxLLTV); - vm.stopPrank(); + borrowableAsset.setBalance(address(this), amountLent); + market.modifyDeposit(int(amountLent), bucket); vm.startPrank(borrower); - market.modifyBorrow(int(amountBorrowed), maxLLTV); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyBorrow(-int(amountRepaid), maxLLTV); + market.modifyBorrow(int(amountBorrowed), bucket); + market.modifyBorrow(-int(amountRepaid), bucket); vm.stopPrank(); assertApproxEqAbs( - market.borrowShare(borrower, maxLLTV), - (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, - 1e3, - "borrow balance borrower" - ); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid, "token balance borrower"); - assertEq( - borrowableAsset.balanceOf(address(market)), - amountLent - amountBorrowed + amountRepaid, - "token balance market" + market.borrowShare(borrower, bucket), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3 ); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid); + assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed + amountRepaid); } - function testDepositCollateral(address user, uint amount, uint lLTV) public { - vm.assume(lLTV < N); - - collateralAsset.setBalance(user, amount); + function testDepositCollateral(uint amount, uint bucket) public { + vm.assume(bucket < N); vm.assume(int(amount) >= 0); - vm.assume(user != address(market)); - vm.startPrank(user); - collateralAsset.approve(address(market), type(uint).max); - market.modifyCollateral(int(amount), lLTV); - vm.stopPrank(); + collateralAsset.setBalance(address(this), amount); + market.modifyCollateral(int(amount), bucket); - assertEq(market.collateral(user, lLTV), amount); - assertEq(collateralAsset.balanceOf(user), 0); + assertEq(market.collateral(address(this), bucket), amount); + assertEq(collateralAsset.balanceOf(address(this)), 0); assertEq(collateralAsset.balanceOf(address(market)), amount); } - function testWithdrawCollateral(address user, uint amountDeposited, uint amountWithdrawn, uint lLTV) public { - vm.assume(lLTV < N); + function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn, uint bucket) public { + vm.assume(bucket < N); vm.assume(amountDeposited >= amountWithdrawn); vm.assume(int(amountDeposited) >= 0); - vm.assume(user != address(market)); - - collateralAsset.setBalance(user, amountDeposited); - - vm.startPrank(user); - collateralAsset.approve(address(market), type(uint).max); - market.modifyCollateral(int(amountDeposited), lLTV); - vm.stopPrank(); - vm.prank(user); - market.modifyCollateral(-int(amountWithdrawn), lLTV); + collateralAsset.setBalance(address(this), amountDeposited); + market.modifyCollateral(int(amountDeposited), bucket); + market.modifyCollateral(-int(amountWithdrawn), bucket); - assertEq(market.collateral(user, lLTV), amountDeposited - amountWithdrawn); - assertEq(collateralAsset.balanceOf(user), amountWithdrawn); + assertEq(market.collateral(address(this), bucket), amountDeposited - amountWithdrawn); + assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn); assertEq(collateralAsset.balanceOf(address(market)), amountDeposited - amountWithdrawn); } - function testTwoUsersSupply( - address firstUser, - uint firstAmount, - uint maxLLTV, - address secondUser, - uint secondAmount - ) public { - vm.assume(firstUser != secondUser); - vm.assume(firstUser != address(market)); - vm.assume(secondUser != address(market)); - vm.assume(maxLLTV < N); + function testTwoUsersSupply(uint firstAmount, uint secondAmount, uint bucket) public { + vm.assume(bucket < N); firstAmount = bound(firstAmount, 1, 2 ** 64); secondAmount = bound(secondAmount, 0, 2 ** 64); - borrowableAsset.setBalance(firstUser, firstAmount); - vm.startPrank(firstUser); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(firstAmount), maxLLTV); - vm.stopPrank(); + borrowableAsset.setBalance(address(this), firstAmount); + market.modifyDeposit(int(firstAmount), bucket); - borrowableAsset.setBalance(secondUser, secondAmount); - vm.startPrank(secondUser); - borrowableAsset.approve(address(market), type(uint).max); - market.modifyDeposit(int(secondAmount), maxLLTV); - vm.stopPrank(); + borrowableAsset.setBalance(borrower, secondAmount); + vm.prank(borrower); + market.modifyDeposit(int(secondAmount), bucket); - assertEq(market.supplyShare(firstUser, maxLLTV), 1e18, "first user supply balance"); - assertEq( - market.supplyShare(secondUser, maxLLTV), secondAmount * 1e18 / firstAmount, "second user supply balance" - ); + assertEq(market.supplyShare(address(this), bucket), 1e18); + assertEq(market.supplyShare(borrower, bucket), secondAmount * 1e18 / firstAmount); + } + + function testModifyDepositUnknownBucket(uint bucket) public { + vm.assume(bucket > N); + vm.expectRevert("unknown bucket"); + market.modifyDeposit(1, bucket); + } + + function testModifyBorrowUnknownBucket(uint bucket) public { + vm.assume(bucket > N); + vm.expectRevert("unknown bucket"); + market.modifyBorrow(1, bucket); + } + + function testModifyCollateralUnknownBucket(uint bucket) public { + vm.assume(bucket > N); + vm.expectRevert("unknown bucket"); + market.modifyCollateral(1, bucket); } } From d57e43423c5171215f1493d8bbae509b5a3cc27d Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 27 Jun 2023 15:05:16 +0200 Subject: [PATCH 005/112] fix: minor issues in MathLib --- src/libraries/MathLib.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index 2ba196b34..2419359da 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -5,24 +5,24 @@ pragma solidity ^0.8.0; library MathLib { uint internal constant WAD = 1e18; - /// @dev Rounds down. + /// @dev Rounds towards zero. function wMul(uint x, uint y) internal pure returns (uint z) { - z = x * y / WAD; + z = (x * y) / WAD; } - /// @dev Rounds down. + /// @dev Rounds towards zero. function wMul(int x, uint y) internal pure returns (int z) { - z = x * int(y) / int(WAD); + z = (x * int(y)) / int(WAD); } - /// @dev Rounds down. + /// @dev Rounds towards zero. function wDiv(uint x, uint y) internal pure returns (uint z) { - z = x * WAD / y; + z = (x * WAD) / y; } - /// @dev Rounds down. + /// @dev Rounds towards zero. function wDiv(int x, uint y) internal pure returns (int z) { - z = x * int(WAD) / int(y); + z = (x * int(WAD)) / int(y); } /// @dev Reverts if x is negative. From 9deb65c37ec9ccd561979ed70e01fbf31d63b0da Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 27 Jun 2023 15:13:42 +0200 Subject: [PATCH 006/112] fix: lastUpdate issue --- src/Market.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 725f6751f..22a38525a 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -50,7 +50,7 @@ contract Market { // Market total borrow shares. mapping(uint => uint) public totalBorrowShares; // Interests last update. - uint public lastUpdate; + mapping(uint => uint) public lastUpdate; // Constructor. @@ -143,11 +143,11 @@ contract Market { if (bucketTotalSupply == 0) return; uint utilization = bucketTotalBorrow.wDiv(bucketTotalSupply); uint borrowRate = irm(utilization); - uint accruedInterests = bucketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate); + uint accruedInterests = bucketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[bucket]); totalSupply[bucket] = bucketTotalSupply + accruedInterests; totalBorrow[bucket] = bucketTotalBorrow + accruedInterests; - lastUpdate = block.timestamp; + lastUpdate[bucket] = block.timestamp; } // Health check. From 9ba8b9c4f5684d6471264dfbc0319890a6c52ca0 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 27 Jun 2023 15:31:11 +0200 Subject: [PATCH 007/112] fix: minor issues --- src/Market.sol | 5 +++-- src/mocks/OracleMock.sol | 2 +- test/forge/Market.t.sol | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 22a38525a..8312dc7b3 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -10,7 +10,7 @@ import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; uint constant N = 10; function bucketToLLTV(uint bucket) pure returns (uint) { - return MathLib.wDiv(bucket, N - 1); + return MathLib.wDiv(bucket + 1, N + 1); } function irm(uint utilization) pure returns (uint) { @@ -153,7 +153,8 @@ contract Market { // Health check. function checkHealth(address user, uint bucket) public view { - if (totalBorrowShares[bucket] > 0 && borrowShare[user][bucket] > 0) { + if (borrowShare[user][bucket] > 0) { + // totalBorrowShares[bucket] > 0 because borrowShare[user][bucket] > 0. uint borrowValue = borrowShare[user][bucket].wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]).wMul( IOracle(borrowableOracle).price() ); diff --git a/src/mocks/OracleMock.sol b/src/mocks/OracleMock.sol index e32e6d22b..0199c850f 100644 --- a/src/mocks/OracleMock.sol +++ b/src/mocks/OracleMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import {IOracle} from "src/Market.sol"; +import {IOracle} from "src/interfaces/IOracle.sol"; contract OracleMock is IOracle { uint public price; diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index af1308793..d28c1c5bd 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -117,9 +117,12 @@ contract MarketTest is Test { vm.prank(borrower); market.modifyBorrow(int(amountBorrowed), bucket); - // Should revert because not enough liquidity. if (amountWithdrawn > amountLent - amountBorrowed) { - vm.expectRevert(); + if (amountWithdrawn > amountLent) { + vm.expectRevert(); + } else { + vm.expectRevert("not enough liquidity"); + } market.modifyDeposit(-int(amountWithdrawn), bucket); return; } From 624ebf643e135798d7b0d602d8dedbb54c416de6 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 27 Jun 2023 15:36:02 +0200 Subject: [PATCH 008/112] chore: commit remappings.txt --- remappings.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 remappings.txt diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 000000000..2ff87bcb1 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,3 @@ +ds-test/=lib/forge-std/lib/ds-test/src/ +forge-std/=lib/forge-std/src/ +solmate/=lib/solmate/src/ From 06b49b2636d51ec2fcf1e3a8d1b10364b49fe9d7 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 27 Jun 2023 15:57:34 +0200 Subject: [PATCH 009/112] test: remove broken liquidity test --- foundry.toml | 7 +++++++ test/forge/Market.t.sol | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/foundry.toml b/foundry.toml index af26ce773..c332bfbfa 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,6 +4,13 @@ out = "out" libs = ["lib"] via-ir = true +[fuzz] +runs = 2048 + +[invariant] +runs = 2048 +depth = 32 + [fmt] int_types = "short" diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index d28c1c5bd..f4c0f8ecb 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -57,14 +57,6 @@ contract MarketTest is Test { } } - function invariantLiquidity2() public { - uint expectedMinimumBalance; - for (uint bucket; bucket < N; bucket++) { - expectedMinimumBalance += market.totalSupply(bucket) + market.totalBorrow(bucket); - } - assertGe(borrowableAsset.balanceOf(address(market)), expectedMinimumBalance); - } - function testDeposit(uint amount, uint bucket) public { amount = bound(amount, 1, 2 ** 64); vm.assume(bucket < N); From 3d28c317c473f82dda593f60a0819d485df30137 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 27 Jun 2023 17:47:56 +0200 Subject: [PATCH 010/112] feat: working batch liquidations --- src/Market.sol | 64 ++++++++++++++++++++++++++++++++++----- src/libraries/MathLib.sol | 12 +++++++- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 8312dc7b3..4f4ed6790 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -7,6 +7,10 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; +uint constant WAD = 1e18; + +uint constant alpha = 0.5e18; + uint constant N = 10; function bucketToLLTV(uint bucket) pure returns (uint) { @@ -76,8 +80,8 @@ contract Market { accrueInterests(bucket); if (totalSupply[bucket] == 0) { - supplyShare[msg.sender][bucket] = 1e18; - totalSupplyShares[bucket] = 1e18; + supplyShare[msg.sender][bucket] = WAD; + totalSupplyShares[bucket] = WAD; } else { int shares = amount.wMul(totalSupplyShares[bucket]).wDiv(totalSupply[bucket]); supplyShare[msg.sender][bucket] = (int(supplyShare[msg.sender][bucket]) + shares).safeToUint(); @@ -102,8 +106,8 @@ contract Market { accrueInterests(bucket); if (totalBorrow[bucket] == 0) { - borrowShare[msg.sender][bucket] = 1e18; - totalBorrowShares[bucket] = 1e18; + borrowShare[msg.sender][bucket] = WAD; + totalBorrowShares[bucket] = WAD; } else { int shares = amount.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); borrowShare[msg.sender][bucket] = (int(borrowShare[msg.sender][bucket]) + shares).safeToUint(); @@ -114,7 +118,7 @@ contract Market { totalBorrow[bucket] = uint(int(totalBorrow[bucket]) + amount); if (amount > 0) { - checkHealth(msg.sender, bucket); + require(isHealthy(msg.sender, bucket), "not enough collateral"); require(totalBorrow[bucket] <= totalSupply[bucket], "not enough liquidity"); } @@ -130,11 +134,54 @@ contract Market { collateral[msg.sender][bucket] = (int(collateral[msg.sender][bucket]) + amount).safeToUint(); - if (amount < 0) checkHealth(msg.sender, bucket); + require(amount > 0 || isHealthy(msg.sender, bucket), "not enough collateral"); IERC20(collateralAsset).handleTransfer({user: msg.sender, amountIn: amount}); } + struct Liquidation { + address borrower; + uint bucket; + uint maxCollat; + } + + // @dev positive amount to seize. + function batchLiquidate(Liquidation[] memory liquidations) external { + int sumCollat; + int sumBorrow; + + for (uint i; i < liquidations.length; i++) { + Liquidation memory liq = liquidations[i]; + (int collat, int borrow) = _liquidate(liq.borrower, liq.bucket, liq.maxCollat); + sumCollat += collat; + sumBorrow += borrow; + } + + IERC20(borrowableAsset).handleTransfer(msg.sender, sumBorrow); + IERC20(collateralAsset).handleTransfer(msg.sender, sumCollat); + } + + function _liquidate(address borrower, uint bucket, uint maxCollat) internal returns (int collat, int borrow) { + accrueInterests(bucket); + + uint bucketLLTV = bucketToLLTV(bucket); + require(!isHealthy(borrower, bucket), "Cannot liquidate a healthy position"); + + uint incentive = WAD + alpha.wMul(WAD.wDiv(bucketLLTV) - WAD); + uint borrowablePrice = IOracle(borrowableOracle).price(); + uint collateralPrice = IOracle(collateralOracle).price(); + maxCollat = maxCollat.min(collateral[borrower][bucket]); + // Safe to cast to int because maxCollat <= collateral[borrower][bucket] + collat = int(maxCollat); + borrow = -(maxCollat.wMul(collateralPrice).wDiv(incentive).wDiv(borrowablePrice)).safeToInt(); + + int shares = borrow.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); + borrowShare[borrower][bucket] = (int(borrowShare[borrower][bucket]) + shares).safeToUint(); + totalBorrowShares[bucket] = (int(totalBorrowShares[bucket]) + shares).safeToUint(); + totalBorrow[bucket] = (int(totalBorrow[bucket]) + borrow).safeToUint(); + collateral[borrower][bucket] = (int(collateral[borrower][bucket]) + collat).safeToUint(); + } + // Interests management. function accrueInterests(uint bucket) internal { @@ -152,14 +199,15 @@ contract Market { // Health check. - function checkHealth(address user, uint bucket) public view { + function isHealthy(address user, uint bucket) public view returns (bool) { if (borrowShare[user][bucket] > 0) { // totalBorrowShares[bucket] > 0 because borrowShare[user][bucket] > 0. uint borrowValue = borrowShare[user][bucket].wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]).wMul( IOracle(borrowableOracle).price() ); uint collateralValue = collateral[user][bucket].wMul(IOracle(collateralOracle).price()); - require(collateralValue.wMul(bucketToLLTV(bucket)) >= borrowValue, "not enough collateral"); + return collateralValue.wMul(bucketToLLTV(bucket)) >= borrowValue; } + return true; } } diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index 2419359da..10860b07a 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -5,6 +5,10 @@ pragma solidity ^0.8.0; library MathLib { uint internal constant WAD = 1e18; + function min(uint x, uint y) internal pure returns (uint z) { + z = x > y ? x : y; + } + /// @dev Rounds towards zero. function wMul(uint x, uint y) internal pure returns (uint z) { z = (x * y) / WAD; @@ -25,9 +29,15 @@ library MathLib { z = (x * int(WAD)) / int(y); } - /// @dev Reverts if x is negative. + /// @dev Reverts if the input is negative. function safeToUint(int x) internal pure returns (uint z) { require(x >= 0); z = uint(x); } + + /// @dev Reverts if the result is negative. + function safeToInt(uint x) internal pure returns (int z) { + z = int(x); + require(z >= 0); + } } From 4f80f34b1093b7671f373cdbac536b2b7b06f939 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 27 Jun 2023 18:12:32 +0200 Subject: [PATCH 011/112] fix: transfer direction, removing int casting --- src/Market.sol | 16 +++++++--------- src/libraries/MathLib.sol | 6 ------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 4f4ed6790..3eff3c513 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -145,35 +145,33 @@ contract Market { uint maxCollat; } - // @dev positive amount to seize. function batchLiquidate(Liquidation[] memory liquidations) external { int sumCollat; int sumBorrow; for (uint i; i < liquidations.length; i++) { Liquidation memory liq = liquidations[i]; - (int collat, int borrow) = _liquidate(liq.borrower, liq.bucket, liq.maxCollat); + (int collat, int borrow) = liquidate(liq.borrower, liq.bucket, liq.maxCollat); sumCollat += collat; sumBorrow += borrow; } - IERC20(borrowableAsset).handleTransfer(msg.sender, sumBorrow); + IERC20(borrowableAsset).handleTransfer(msg.sender, -sumBorrow); IERC20(collateralAsset).handleTransfer(msg.sender, sumCollat); } - function _liquidate(address borrower, uint bucket, uint maxCollat) internal returns (int collat, int borrow) { + function liquidate(address borrower, uint bucket, uint maxCollat) internal returns (int collat, int borrow) { accrueInterests(bucket); uint bucketLLTV = bucketToLLTV(bucket); - require(!isHealthy(borrower, bucket), "Cannot liquidate a healthy position"); + require(!isHealthy(borrower, bucket), "cannot liquidate a healthy position"); uint incentive = WAD + alpha.wMul(WAD.wDiv(bucketLLTV) - WAD); uint borrowablePrice = IOracle(borrowableOracle).price(); uint collateralPrice = IOracle(collateralOracle).price(); - maxCollat = maxCollat.min(collateral[borrower][bucket]); - // Safe to cast to int because maxCollat <= collateral[borrower][bucket] - collat = int(maxCollat); - borrow = -(maxCollat.wMul(collateralPrice).wDiv(incentive).wDiv(borrowablePrice)).safeToInt(); + // Safe to cast because it's smaller than collateral[borrower][bucket] + collat = -int(maxCollat.min(collateral[borrower][bucket])); + borrow = collat.wMul(collateralPrice).wDiv(incentive).wDiv(borrowablePrice); int shares = borrow.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); borrowShare[borrower][bucket] = (int(borrowShare[borrower][bucket]) + shares).safeToUint(); diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index 10860b07a..1067d4ccc 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -34,10 +34,4 @@ library MathLib { require(x >= 0); z = uint(x); } - - /// @dev Reverts if the result is negative. - function safeToInt(uint x) internal pure returns (int z) { - z = int(x); - require(z >= 0); - } } From 4cb66b18100ca6a7ca635f75f5befb35a77e658a Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 27 Jun 2023 18:13:43 +0200 Subject: [PATCH 012/112] fix: Math.min --- src/libraries/MathLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index 1067d4ccc..cfb4c27b7 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -6,7 +6,7 @@ library MathLib { uint internal constant WAD = 1e18; function min(uint x, uint y) internal pure returns (uint z) { - z = x > y ? x : y; + z = x < y ? x : y; } /// @dev Rounds towards zero. From 2aee83b78a42e19f2060a1737a94d70dd98e3afa Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 27 Jun 2023 18:14:21 +0200 Subject: [PATCH 013/112] fix: comment safeToUint --- src/libraries/MathLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index cfb4c27b7..69c8b053e 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -29,7 +29,7 @@ library MathLib { z = (x * int(WAD)) / int(y); } - /// @dev Reverts if the input is negative. + /// @dev Reverts if `x` is negative. function safeToUint(int x) internal pure returns (uint z) { require(x >= 0); z = uint(x); From 9472ff7dec58670f22fc6013b4c7bb562828e6c7 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 27 Jun 2023 18:29:30 +0200 Subject: [PATCH 014/112] feat: 0 amount liquidation and check bucket --- src/Market.sol | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 3eff3c513..a3fec81a5 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -156,22 +156,26 @@ contract Market { sumBorrow += borrow; } - IERC20(borrowableAsset).handleTransfer(msg.sender, -sumBorrow); IERC20(collateralAsset).handleTransfer(msg.sender, sumCollat); + IERC20(borrowableAsset).handleTransfer(msg.sender, -sumBorrow); } + /// @return collat The negative amount of collateral added. + /// @return borrow The negative amount of borrow added. function liquidate(address borrower, uint bucket, uint maxCollat) internal returns (int collat, int borrow) { + if (maxCollat == 0) return (0, 0); + require(bucket < N, "unknown bucket"); + accrueInterests(bucket); - uint bucketLLTV = bucketToLLTV(bucket); require(!isHealthy(borrower, bucket), "cannot liquidate a healthy position"); - uint incentive = WAD + alpha.wMul(WAD.wDiv(bucketLLTV) - WAD); - uint borrowablePrice = IOracle(borrowableOracle).price(); - uint collateralPrice = IOracle(collateralOracle).price(); + uint incentive = WAD + alpha.wMul(WAD.wDiv(bucketToLLTV(bucket)) - WAD); + uint borrowPrice = IOracle(borrowableOracle).price(); + uint collatPrice = IOracle(collateralOracle).price(); // Safe to cast because it's smaller than collateral[borrower][bucket] collat = -int(maxCollat.min(collateral[borrower][bucket])); - borrow = collat.wMul(collateralPrice).wDiv(incentive).wDiv(borrowablePrice); + borrow = collat.wMul(collatPrice).wDiv(incentive).wDiv(borrowPrice); int shares = borrow.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); borrowShare[borrower][bucket] = (int(borrowShare[borrower][bucket]) + shares).safeToUint(); From 15a8e071e76d1bf0fd4bbe5c22338749c8343b3e Mon Sep 17 00:00:00 2001 From: MathisGD Date: Wed, 28 Jun 2023 00:03:52 +0200 Subject: [PATCH 015/112] fix: empty market issue --- src/Market.sol | 4 ++-- src/libraries/MathLib.sol | 2 +- test/forge/Market.t.sol | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 8312dc7b3..a01f14bc0 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -75,7 +75,7 @@ contract Market { accrueInterests(bucket); - if (totalSupply[bucket] == 0) { + if (totalSupply[bucket] == 0 && amount > 0) { supplyShare[msg.sender][bucket] = 1e18; totalSupplyShares[bucket] = 1e18; } else { @@ -101,7 +101,7 @@ contract Market { accrueInterests(bucket); - if (totalBorrow[bucket] == 0) { + if (totalBorrow[bucket] == 0 && amount > 0) { borrowShare[msg.sender][bucket] = 1e18; totalBorrowShares[bucket] = 1e18; } else { diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index 2419359da..a965c8e5f 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -27,7 +27,7 @@ library MathLib { /// @dev Reverts if x is negative. function safeToUint(int x) internal pure returns (uint z) { - require(x >= 0); + require(x >= 0, "negative"); z = uint(x); } } diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index f4c0f8ecb..db8890422 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -199,12 +199,18 @@ contract MarketTest is Test { function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn, uint bucket) public { vm.assume(bucket < N); - - vm.assume(amountDeposited >= amountWithdrawn); - vm.assume(int(amountDeposited) >= 0); + vm.assume(int(amountDeposited) > 0); + vm.assume(int(amountWithdrawn) > 0); collateralAsset.setBalance(address(this), amountDeposited); market.modifyCollateral(int(amountDeposited), bucket); + + if (amountWithdrawn > amountDeposited) { + vm.expectRevert("negative"); + market.modifyCollateral(-int(amountWithdrawn), bucket); + return; + } + market.modifyCollateral(-int(amountWithdrawn), bucket); assertEq(market.collateral(address(this), bucket), amountDeposited - amountWithdrawn); @@ -245,4 +251,25 @@ contract MarketTest is Test { vm.expectRevert("unknown bucket"); market.modifyCollateral(1, bucket); } + + function testWithdrawEmptyMarket(int amount, uint bucket) public { + vm.assume(bucket < N); + vm.assume(amount < 0); + vm.expectRevert(stdError.divisionError); + market.modifyDeposit(amount, bucket); + } + + function testRepayEmptyMarket(int amount, uint bucket) public { + vm.assume(bucket < N); + vm.assume(amount < 0); + vm.expectRevert(stdError.divisionError); + market.modifyBorrow(amount, bucket); + } + + function testWithdrawCollateralEmptyMarket(int amount, uint bucket) public { + vm.assume(bucket < N); + vm.assume(amount < 0); + vm.expectRevert("negative"); + market.modifyCollateral(amount, bucket); + } } From 5d9137d6b758a4f688ec800ee6f8a61a5bcb0997 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 11:00:26 +0200 Subject: [PATCH 016/112] test: add liquidation test --- src/Market.sol | 21 ++++++----- test/forge/Market.t.sol | 82 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index a3fec81a5..d99870c3f 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -7,6 +7,8 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; +import "forge-std/console.sol"; + uint constant WAD = 1e18; uint constant alpha = 0.5e18; @@ -139,19 +141,20 @@ contract Market { IERC20(collateralAsset).handleTransfer({user: msg.sender, amountIn: amount}); } + // Liquidation. + struct Liquidation { - address borrower; uint bucket; + address borrower; uint maxCollat; } - function batchLiquidate(Liquidation[] memory liquidations) external { - int sumCollat; - int sumBorrow; - - for (uint i; i < liquidations.length; i++) { - Liquidation memory liq = liquidations[i]; - (int collat, int borrow) = liquidate(liq.borrower, liq.bucket, liq.maxCollat); + /// @return sumCollat The negative amount of collateral added. + /// @return sumBorrow The negative amount of borrow added. + function batchLiquidate(Liquidation[] memory liquidationData) external returns (int sumCollat, int sumBorrow) { + for (uint i; i < liquidationData.length; i++) { + Liquidation memory liq = liquidationData[i]; + (int collat, int borrow) = liquidate(liq.bucket, liq.borrower, liq.maxCollat); sumCollat += collat; sumBorrow += borrow; } @@ -162,7 +165,7 @@ contract Market { /// @return collat The negative amount of collateral added. /// @return borrow The negative amount of borrow added. - function liquidate(address borrower, uint bucket, uint maxCollat) internal returns (int collat, int borrow) { + function liquidate(uint bucket, address borrower, uint maxCollat) internal returns (int collat, int borrow) { if (maxCollat == 0) return (0, 0); require(bucket < N, "unknown bucket"); diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index f4c0f8ecb..49bed7aeb 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -12,6 +12,7 @@ contract MarketTest is Test { using MathLib for uint; address private constant borrower = address(1234); + address private constant liquidator = address(5678); Market private market; ERC20 private borrowableAsset; @@ -44,6 +45,30 @@ contract MarketTest is Test { vm.stopPrank(); } + // To move to a test utils file later. + + function networth(address user) internal view returns (uint) { + uint collateralAssetValue = collateralAsset.balanceOf(user).wMul(collateralOracle.price()); + uint borrowableAssetValue = borrowableAsset.balanceOf(user).wMul(borrowableOracle.price()); + return collateralAssetValue + borrowableAssetValue; + } + + function supplyBalance(uint bucket, address user) internal view returns (uint) { + uint supplyShares = market.supplyShare(user, bucket); + uint totalShares = market.totalSupplyShares(bucket); + uint totalSupply = market.totalSupply(bucket); + return supplyShares.wMul(totalSupply).wDiv(totalShares); + } + + function borrowBalance(uint bucket, address user) internal view returns (uint) { + uint borrowerShares = market.borrowShare(user, bucket); + uint totalShares = market.totalBorrowShares(bucket); + uint totalBorrow = market.totalBorrow(bucket); + return borrowerShares.wMul(totalBorrow).wDiv(totalShares); + } + + // Invariants + function invariantParams() public { assertEq(market.borrowableAsset(), address(borrowableAsset)); assertEq(market.collateralAsset(), address(collateralAsset)); @@ -57,6 +82,8 @@ contract MarketTest is Test { } } + // Tests + function testDeposit(uint amount, uint bucket) public { amount = bound(amount, 1, 2 ** 64); vm.assume(bucket < N); @@ -212,6 +239,61 @@ contract MarketTest is Test { assertEq(collateralAsset.balanceOf(address(market)), amountDeposited - amountWithdrawn); } + function testLiquidate(uint bucket, uint amountLent) public { + borrowableOracle.setPrice(1e18); + amountLent = bound(amountLent, 1000, 2 ** 64); + vm.assume(bucket < N); + + uint amountCollateral = amountLent; + uint lLTV = bucketToLLTV(bucket); + uint borrowingPower = amountCollateral.wMul(lLTV); + uint amountBorrowed = borrowingPower.wMul(0.8e18); + uint maxCollat = amountCollateral.wMul(lLTV); + + borrowableAsset.setBalance(address(this), amountLent); + collateralAsset.setBalance(borrower, amountCollateral); + borrowableAsset.setBalance(liquidator, amountBorrowed); + + // Lend + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amountLent), bucket); + + // Borrow + vm.startPrank(borrower); + collateralAsset.approve(address(market), type(uint).max); + market.modifyCollateral(int(amountCollateral), bucket); + market.modifyBorrow(int(amountBorrowed), bucket); + vm.stopPrank(); + + // Price change + borrowableOracle.setPrice(2e18); + + uint liquidatorNetWorthBefore = networth(liquidator); + + // Liquidate + Market.Liquidation[] memory liquidationData = new Market.Liquidation[](1); + liquidationData[0] = Market.Liquidation(bucket, borrower, maxCollat); + vm.startPrank(liquidator); + borrowableAsset.approve(address(market), type(uint).max); + (int sumCollat, int sumBorrow) = market.batchLiquidate(liquidationData); + vm.stopPrank(); + + uint liquidatorNetWorthAfter = networth(liquidator); + + assertGt(liquidatorNetWorthAfter, liquidatorNetWorthBefore, "liquidator's networth"); + assertLt(sumCollat, 0, "collateral seized"); + assertLt(sumBorrow, 0, "borrow repaid"); + assertApproxEqAbs( + int(borrowBalance(bucket, borrower)), int(amountBorrowed) + sumBorrow, 100, "collateral balance borrower" + ); + assertApproxEqAbs( + int(market.collateral(borrower, bucket)), + int(amountCollateral) + sumCollat, + 100, + "collateral balance borrower" + ); + } + function testTwoUsersSupply(uint firstAmount, uint secondAmount, uint bucket) public { vm.assume(bucket < N); firstAmount = bound(firstAmount, 1, 2 ** 64); From 15a6ef87295a9e84f035d0dc8d095cce20f81341 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 11:12:31 +0200 Subject: [PATCH 017/112] test: added balance check when two users supply --- test/forge/Market.t.sol | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index d64f689bd..e7df1e7b0 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -274,7 +274,7 @@ contract MarketTest is Test { // Price change borrowableOracle.setPrice(2e18); - uint liquidatorNetWorthBefore = networth(liquidator); + uint liquidatorNetworthBefore = networth(liquidator); // Liquidate Market.Liquidation[] memory liquidationData = new Market.Liquidation[](1); @@ -284,9 +284,9 @@ contract MarketTest is Test { (int sumCollat, int sumBorrow) = market.batchLiquidate(liquidationData); vm.stopPrank(); - uint liquidatorNetWorthAfter = networth(liquidator); + uint liquidatorNetworthAfter = networth(liquidator); - assertGt(liquidatorNetWorthAfter, liquidatorNetWorthBefore, "liquidator's networth"); + assertGt(liquidatorNetworthAfter, liquidatorNetworthBefore, "liquidator's networth"); assertLt(sumCollat, 0, "collateral seized"); assertLt(sumBorrow, 0, "borrow repaid"); assertApproxEqAbs( @@ -312,8 +312,10 @@ contract MarketTest is Test { vm.prank(borrower); market.modifyDeposit(int(secondAmount), bucket); - assertEq(market.supplyShare(address(this), bucket), 1e18); - assertEq(market.supplyShare(borrower, bucket), secondAmount * 1e18 / firstAmount); + assertApproxEqAbs(supplyBalance(bucket, address(this)), firstAmount, 100, "same balance first user"); + assertEq(market.supplyShare(address(this), bucket), 1e18, "expected shares first user"); + assertApproxEqAbs(supplyBalance(bucket, borrower), secondAmount, 100, "same balance second user"); + assertEq(market.supplyShare(borrower, bucket), secondAmount * 1e18 / firstAmount, "expected shares second user"); } function testModifyDepositUnknownBucket(uint bucket) public { From b993b9e38e921c134909bc1cc18f6be70bc56ede Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 11:48:49 +0200 Subject: [PATCH 018/112] ci: change name --- .github/workflows/test.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2a54a0dc4..236d3cd2a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: test +name: Foundry CI on: workflow_dispatch: @@ -7,15 +7,11 @@ on: - main pull_request: -env: - FOUNDRY_PROFILE: ci - jobs: - check: + test: strategy: fail-fast: true - name: Foundry project runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From f8e9cd527605b5e0ad4215318c9311f28e5a197c Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 27 Jun 2023 12:52:03 +0200 Subject: [PATCH 019/112] feat: multiple markets --- src/Blue.sol | 162 +++++++++++++++++++++ src/Market.sol | 165 ---------------------- test/forge/Blue.t.sol | 265 ++++++++++++++++++++++++++++++++++ test/forge/Market.t.sol | 275 ------------------------------------ test/hardhat/Market.spec.ts | 91 ++++++++---- 5 files changed, 487 insertions(+), 471 deletions(-) create mode 100644 src/Blue.sol delete mode 100644 src/Market.sol create mode 100644 test/forge/Blue.t.sol delete mode 100644 test/forge/Market.t.sol diff --git a/src/Blue.sol b/src/Blue.sol new file mode 100644 index 000000000..a9b17b3fa --- /dev/null +++ b/src/Blue.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC20} from "src/interfaces/IERC20.sol"; +import {IOracle} from "src/interfaces/IOracle.sol"; + +import {MathLib} from "src/libraries/MathLib.sol"; +import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; + +// Market id. +type Id is bytes32; + +// Market info. +struct Info { + IERC20 borrowableAsset; + IERC20 collateralAsset; + IOracle borrowableOracle; + IOracle collateralOracle; + uint lLTV; +} + +function irm(uint utilization) pure returns (uint) { + // Divide by the number of seconds in a year. + // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. + return utilization / 365 days; +} + +contract Blue { + using MathLib for int; + using MathLib for uint; + using SafeTransferLib for IERC20; + + // Storage. + + // User' supply balances. + mapping(Id => mapping(address => uint)) public supplyShare; + // User' borrow balances. + mapping(Id => mapping(address => uint)) public borrowShare; + // User' collateral balance. + mapping(Id => mapping(address => uint)) public collateral; + // Market total supply. + mapping(Id => uint) public totalSupply; + // Market total supply shares. + mapping(Id => uint) public totalSupplyShares; + // Market total borrow. + mapping(Id => uint) public totalBorrow; + // Market total borrow shares. + mapping(Id => uint) public totalBorrowShares; + // Interests last update (used to check if a market has been created). + mapping(Id => uint) public lastUpdate; + + // Markets management. + + function createMarket(Info calldata info) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + require(lastUpdate[id] == 0, "market already exists"); + + accrueInterests(id); + } + + // Suppliers position management. + + /// @dev positive amount to deposit. + function modifyDeposit(Info calldata info, int amount) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + if (amount == 0) return; + require(lastUpdate[id] != 0, "unknown market"); + + accrueInterests(id); + + if (totalSupply[id] == 0 && amount > 0) { + supplyShare[id][msg.sender] = 1e18; + totalSupplyShares[id] = 1e18; + } else { + int shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); + supplyShare[id][msg.sender] = (int(supplyShare[id][msg.sender]) + shares).safeToUint(); + totalSupplyShares[id] = (int(totalSupplyShares[id]) + shares).safeToUint(); + } + + // No need to check if the integer is positive. + totalSupply[id] = uint(int(totalSupply[id]) + amount); + + if (amount < 0) require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); + + info.borrowableAsset.handleTransfer({user: msg.sender, amountIn: amount}); + } + + // Borrowers position management. + + /// @dev positive amount to borrow (to discuss). + function modifyBorrow(Info calldata info, int amount) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + if (amount == 0) return; + require(lastUpdate[id] != 0, "unknown market"); + + accrueInterests(id); + + if (totalBorrow[id] == 0 && amount > 0) { + borrowShare[id][msg.sender] = 1e18; + totalBorrowShares[id] = 1e18; + } else { + int shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + borrowShare[id][msg.sender] = (int(borrowShare[id][msg.sender]) + shares).safeToUint(); + totalBorrowShares[id] = (int(totalBorrowShares[id]) + shares).safeToUint(); + } + + // No need to check if the integer is positive. + totalBorrow[id] = uint(int(totalBorrow[id]) + amount); + + if (amount > 0) { + checkHealth(info, id, msg.sender); + require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); + } + + info.borrowableAsset.handleTransfer({user: msg.sender, amountIn: -amount}); + } + + /// @dev positive amount to deposit. + function modifyCollateral(Info calldata info, int amount) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + if (amount == 0) return; + require(lastUpdate[id] != 0, "unknown market"); + + accrueInterests(id); + + collateral[id][msg.sender] = (int(collateral[id][msg.sender]) + amount).safeToUint(); + + if (amount < 0) checkHealth(info, id, msg.sender); + + info.collateralAsset.handleTransfer({user: msg.sender, amountIn: amount}); + } + + // Interests management. + + function accrueInterests(Id id) internal { + uint bucketTotalSupply = totalSupply[id]; + + if (bucketTotalSupply != 0) { + uint bucketTotalBorrow = totalBorrow[id]; + uint utilization = bucketTotalBorrow.wDiv(bucketTotalSupply); + uint borrowRate = irm(utilization); + uint accruedInterests = bucketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); + totalSupply[id] = bucketTotalSupply + accruedInterests; + totalBorrow[id] = bucketTotalBorrow + accruedInterests; + } + + lastUpdate[id] = block.timestamp; + } + + // Health check. + + function checkHealth(Info calldata info, Id id, address user) public view { + if (borrowShare[id][user] > 0) { + // totalBorrowShares[bucket] > 0 because borrowShare[user][bucket] > 0. + uint borrowValue = borrowShare[id][user].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul( + IOracle(info.borrowableOracle).price() + ); + uint collateralValue = collateral[id][user].wMul(IOracle(info.collateralOracle).price()); + require(collateralValue.wMul(info.lLTV) >= borrowValue, "not enough collateral"); + } + } +} diff --git a/src/Market.sol b/src/Market.sol deleted file mode 100644 index a01f14bc0..000000000 --- a/src/Market.sol +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {IERC20} from "src/interfaces/IERC20.sol"; -import {IOracle} from "src/interfaces/IOracle.sol"; - -import {MathLib} from "src/libraries/MathLib.sol"; -import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; - -uint constant N = 10; - -function bucketToLLTV(uint bucket) pure returns (uint) { - return MathLib.wDiv(bucket + 1, N + 1); -} - -function irm(uint utilization) pure returns (uint) { - // Divide by the number of seconds in a year. - // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. - return utilization / 365 days; -} - -contract Market { - using MathLib for int; - using MathLib for uint; - using SafeTransferLib for IERC20; - - // Constants. - - uint public constant getN = N; - - address public immutable borrowableAsset; - address public immutable collateralAsset; - address public immutable borrowableOracle; - address public immutable collateralOracle; - - // Storage. - - // User' supply balances. - mapping(address => mapping(uint => uint)) public supplyShare; - // User' borrow balances. - mapping(address => mapping(uint => uint)) public borrowShare; - // User' collateral balance. - mapping(address => mapping(uint => uint)) public collateral; - // Market total supply. - mapping(uint => uint) public totalSupply; - // Market total supply shares. - mapping(uint => uint) public totalSupplyShares; - // Market total borrow. - mapping(uint => uint) public totalBorrow; - // Market total borrow shares. - mapping(uint => uint) public totalBorrowShares; - // Interests last update. - mapping(uint => uint) public lastUpdate; - - // Constructor. - - constructor( - address newBorrowableAsset, - address newCollateralAsset, - address newBorrowableOracle, - address newCollateralOracle - ) { - borrowableAsset = newBorrowableAsset; - collateralAsset = newCollateralAsset; - borrowableOracle = newBorrowableOracle; - collateralOracle = newCollateralOracle; - } - - // Suppliers position management. - - /// @dev positive amount to deposit. - function modifyDeposit(int amount, uint bucket) external { - if (amount == 0) return; - require(bucket < N, "unknown bucket"); - - accrueInterests(bucket); - - if (totalSupply[bucket] == 0 && amount > 0) { - supplyShare[msg.sender][bucket] = 1e18; - totalSupplyShares[bucket] = 1e18; - } else { - int shares = amount.wMul(totalSupplyShares[bucket]).wDiv(totalSupply[bucket]); - supplyShare[msg.sender][bucket] = (int(supplyShare[msg.sender][bucket]) + shares).safeToUint(); - totalSupplyShares[bucket] = (int(totalSupplyShares[bucket]) + shares).safeToUint(); - } - - // No need to check if the integer is positive. - totalSupply[bucket] = uint(int(totalSupply[bucket]) + amount); - - if (amount < 0) require(totalBorrow[bucket] <= totalSupply[bucket], "not enough liquidity"); - - IERC20(borrowableAsset).handleTransfer({user: msg.sender, amountIn: amount}); - } - - // Borrowers position management. - - /// @dev positive amount to borrow (to discuss). - function modifyBorrow(int amount, uint bucket) external { - if (amount == 0) return; - require(bucket < N, "unknown bucket"); - - accrueInterests(bucket); - - if (totalBorrow[bucket] == 0 && amount > 0) { - borrowShare[msg.sender][bucket] = 1e18; - totalBorrowShares[bucket] = 1e18; - } else { - int shares = amount.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); - borrowShare[msg.sender][bucket] = (int(borrowShare[msg.sender][bucket]) + shares).safeToUint(); - totalBorrowShares[bucket] = (int(totalBorrowShares[bucket]) + shares).safeToUint(); - } - - // No need to check if the integer is positive. - totalBorrow[bucket] = uint(int(totalBorrow[bucket]) + amount); - - if (amount > 0) { - checkHealth(msg.sender, bucket); - require(totalBorrow[bucket] <= totalSupply[bucket], "not enough liquidity"); - } - - IERC20(borrowableAsset).handleTransfer({user: msg.sender, amountIn: -amount}); - } - - /// @dev positive amount to deposit. - function modifyCollateral(int amount, uint bucket) external { - if (amount == 0) return; - require(bucket < N, "unknown bucket"); - - accrueInterests(bucket); - - collateral[msg.sender][bucket] = (int(collateral[msg.sender][bucket]) + amount).safeToUint(); - - if (amount < 0) checkHealth(msg.sender, bucket); - - IERC20(collateralAsset).handleTransfer({user: msg.sender, amountIn: amount}); - } - - // Interests management. - - function accrueInterests(uint bucket) internal { - uint bucketTotalBorrow = totalBorrow[bucket]; - uint bucketTotalSupply = totalSupply[bucket]; - if (bucketTotalSupply == 0) return; - uint utilization = bucketTotalBorrow.wDiv(bucketTotalSupply); - uint borrowRate = irm(utilization); - uint accruedInterests = bucketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[bucket]); - - totalSupply[bucket] = bucketTotalSupply + accruedInterests; - totalBorrow[bucket] = bucketTotalBorrow + accruedInterests; - lastUpdate[bucket] = block.timestamp; - } - - // Health check. - - function checkHealth(address user, uint bucket) public view { - if (borrowShare[user][bucket] > 0) { - // totalBorrowShares[bucket] > 0 because borrowShare[user][bucket] > 0. - uint borrowValue = borrowShare[user][bucket].wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]).wMul( - IOracle(borrowableOracle).price() - ); - uint collateralValue = collateral[user][bucket].wMul(IOracle(collateralOracle).price()); - require(collateralValue.wMul(bucketToLLTV(bucket)) >= borrowValue, "not enough collateral"); - } - } -} diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol new file mode 100644 index 000000000..368a454db --- /dev/null +++ b/test/forge/Blue.t.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC20} from "src/interfaces/IERC20.sol"; +import {IOracle} from "src/interfaces/IOracle.sol"; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import "src/Blue.sol"; +import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; +import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; + +contract BlueTest is Test { + using MathLib for uint; + + address private constant borrower = address(1234); + uint private constant lLTV = 0.8 ether; + + Blue private blue; + ERC20 private borrowableAsset; + ERC20 private collateralAsset; + Oracle private borrowableOracle; + Oracle private collateralOracle; + Info public info; + Id public id; + + function setUp() public { + // Create Blue. + blue = new Blue(); + + // List a market. + borrowableAsset = new ERC20("borrowable", "B", 18); + collateralAsset = new ERC20("collateral", "C", 18); + borrowableOracle = new Oracle(); + collateralOracle = new Oracle(); + info = Info( + IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, lLTV + ); + id = Id.wrap(keccak256(abi.encode(info))); + + blue.createMarket(info); + + // We set the price of the borrowable asset to zero so that borrowers + // don't need to deposit any collateral. + borrowableOracle.setPrice(0); + collateralOracle.setPrice(1e18); + + borrowableAsset.approve(address(blue), type(uint).max); + collateralAsset.approve(address(blue), type(uint).max); + vm.startPrank(borrower); + borrowableAsset.approve(address(blue), type(uint).max); + collateralAsset.approve(address(blue), type(uint).max); + vm.stopPrank(); + } + + function invariantLiquidity() public { + assertLe(blue.totalBorrow(id), blue.totalSupply(id)); + } + + function testDeposit(uint amount) public { + amount = bound(amount, 1, 2 ** 64); + + borrowableAsset.setBalance(address(this), amount); + blue.modifyDeposit(info, int(amount)); + + assertEq(blue.supplyShare(id, address(this)), 1e18); + assertEq(borrowableAsset.balanceOf(address(this)), 0); + assertEq(borrowableAsset.balanceOf(address(blue)), amount); + } + + function testBorrow(uint amountLent, uint amountBorrowed) public { + amountLent = bound(amountLent, 0, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); + + borrowableAsset.setBalance(address(this), amountLent); + blue.modifyDeposit(info, int(amountLent)); + + if (amountBorrowed == 0) { + blue.modifyBorrow(info, int(amountBorrowed)); + return; + } + + if (amountBorrowed > amountLent) { + vm.prank(borrower); + vm.expectRevert("not enough liquidity"); + blue.modifyBorrow(info, int(amountBorrowed)); + return; + } + + vm.prank(borrower); + blue.modifyBorrow(info, int(amountBorrowed)); + + assertEq(blue.borrowShare(id, borrower), 1e18); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed); + } + + function testWithdraw(uint amountLent, uint amountWithdrawn, uint amountBorrowed) public { + amountLent = bound(amountLent, 1, 2 ** 64); + vm.assume(amountLent >= amountBorrowed); + vm.assume(int(amountWithdrawn) >= 0); + + borrowableAsset.setBalance(address(this), amountLent); + blue.modifyDeposit(info, int(amountLent)); + + vm.prank(borrower); + blue.modifyBorrow(info, int(amountBorrowed)); + + if (amountWithdrawn > amountLent - amountBorrowed) { + if (amountWithdrawn > amountLent) { + vm.expectRevert(); + } else { + vm.expectRevert("not enough liquidity"); + } + blue.modifyDeposit(info, -int(amountWithdrawn)); + return; + } + + blue.modifyDeposit(info, -int(amountWithdrawn)); + + assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3); + assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn); + } + + function testCollateralRequirements( + uint amountCollateral, + uint amountBorrowed, + uint priceCollateral, + uint priceBorrowable + ) public { + amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); + priceBorrowable = bound(priceBorrowable, 0, 2 ** 64); + amountCollateral = bound(amountCollateral, 0, 2 ** 64); + priceCollateral = bound(priceCollateral, 0, 2 ** 64); + + borrowableOracle.setPrice(priceBorrowable); + collateralOracle.setPrice(priceCollateral); + + borrowableAsset.setBalance(address(this), amountBorrowed); + collateralAsset.setBalance(borrower, amountCollateral); + + blue.modifyDeposit(info, int(amountBorrowed)); + + vm.prank(borrower); + blue.modifyCollateral(info, int(amountCollateral)); + + uint collateralValue = amountCollateral.wMul(priceCollateral); + uint borrowValue = amountBorrowed.wMul(priceBorrowable); + if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(lLTV))) { + vm.prank(borrower); + blue.modifyBorrow(info, int(amountBorrowed)); + } else { + vm.prank(borrower); + vm.expectRevert("not enough collateral"); + blue.modifyBorrow(info, int(amountBorrowed)); + } + } + + function testRepay(uint amountLent, uint amountBorrowed, uint amountRepaid) public { + amountLent = bound(amountLent, 1, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 1, amountLent); + amountRepaid = bound(amountRepaid, 0, amountBorrowed); + + borrowableAsset.setBalance(address(this), amountLent); + blue.modifyDeposit(info, int(amountLent)); + + vm.startPrank(borrower); + blue.modifyBorrow(info, int(amountBorrowed)); + blue.modifyBorrow(info, -int(amountRepaid)); + vm.stopPrank(); + + assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid); + } + + function testDepositCollateral(uint amount) public { + vm.assume(int(amount) >= 0); + + collateralAsset.setBalance(address(this), amount); + blue.modifyCollateral(info, int(amount)); + + assertEq(blue.collateral(id, address(this)), amount); + assertEq(collateralAsset.balanceOf(address(this)), 0); + assertEq(collateralAsset.balanceOf(address(blue)), amount); + } + + function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn) public { + vm.assume(int(amountDeposited) > 0); + vm.assume(int(amountWithdrawn) > 0); + + collateralAsset.setBalance(address(this), amountDeposited); + blue.modifyCollateral(info, int(amountDeposited)); + + if (amountWithdrawn > amountDeposited) { + vm.expectRevert("negative"); + blue.modifyCollateral(info, -int(amountWithdrawn)); + return; + } + + blue.modifyCollateral(info, -int(amountWithdrawn)); + + assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn); + assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn); + assertEq(collateralAsset.balanceOf(address(blue)), amountDeposited - amountWithdrawn); + } + + function testTwoUsersSupply(uint firstAmount, uint secondAmount) public { + firstAmount = bound(firstAmount, 1, 2 ** 64); + secondAmount = bound(secondAmount, 0, 2 ** 64); + + borrowableAsset.setBalance(address(this), firstAmount); + blue.modifyDeposit(info, int(firstAmount)); + + borrowableAsset.setBalance(borrower, secondAmount); + vm.prank(borrower); + blue.modifyDeposit(info, int(secondAmount)); + + assertEq(blue.supplyShare(id, address(this)), 1e18); + assertEq(blue.supplyShare(id, borrower), secondAmount * 1e18 / firstAmount); + } + + function testModifyDepositUnknownMarket(Info memory infoFuzz) public { + vm.assume(neq(infoFuzz, info)); + vm.expectRevert("unknown market"); + blue.modifyDeposit(infoFuzz, 1); + } + + function testModifyBorrowUnknownMarket(Info memory infoFuzz) public { + vm.assume(neq(infoFuzz, info)); + vm.expectRevert("unknown market"); + blue.modifyBorrow(infoFuzz, 1); + } + + function testModifyCollateralUnknownMarket(Info memory infoFuzz) public { + vm.assume(neq(infoFuzz, info)); + vm.expectRevert("unknown market"); + blue.modifyCollateral(infoFuzz, 1); + } + + function testWithdrawEmptyMarket(int amount) public { + vm.assume(amount < 0); + vm.expectRevert(stdError.divisionError); + blue.modifyDeposit(info, amount); + } + + function testRepayEmptyMarket(int amount) public { + vm.assume(amount < 0); + vm.expectRevert(stdError.divisionError); + blue.modifyBorrow(info, amount); + } + + function testWithdrawCollateralEmptyMarket(int amount) public { + vm.assume(amount < 0); + vm.expectRevert("negative"); + blue.modifyCollateral(info, amount); + } +} + +function neq(Info memory a, Info memory b) pure returns (bool) { + return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset + || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV; +} diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol deleted file mode 100644 index db8890422..000000000 --- a/test/forge/Market.t.sol +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import "src/Market.sol"; -import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; -import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; - -contract MarketTest is Test { - using MathLib for uint; - - address private constant borrower = address(1234); - - Market private market; - ERC20 private borrowableAsset; - ERC20 private collateralAsset; - Oracle private borrowableOracle; - Oracle private collateralOracle; - - function setUp() public { - borrowableAsset = new ERC20("borrowable", "B", 18); - collateralAsset = new ERC20("collateral", "C", 18); - borrowableOracle = new Oracle(); - collateralOracle = new Oracle(); - market = new Market( - address(borrowableAsset), - address(collateralAsset), - address(borrowableOracle), - address(collateralOracle) - ); - - // We set the price of the borrowable asset to zero so that borrowers - // don't need to deposit any collateral. - borrowableOracle.setPrice(0); - collateralOracle.setPrice(1e18); - - borrowableAsset.approve(address(market), type(uint).max); - collateralAsset.approve(address(market), type(uint).max); - vm.startPrank(borrower); - borrowableAsset.approve(address(market), type(uint).max); - collateralAsset.approve(address(market), type(uint).max); - vm.stopPrank(); - } - - function invariantParams() public { - assertEq(market.borrowableAsset(), address(borrowableAsset)); - assertEq(market.collateralAsset(), address(collateralAsset)); - assertEq(market.borrowableOracle(), address(borrowableOracle)); - assertEq(market.collateralOracle(), address(collateralOracle)); - } - - function invariantLiquidity() public { - for (uint bucket; bucket < N; bucket++) { - assertLe(market.totalBorrow(bucket), market.totalSupply(bucket)); - } - } - - function testDeposit(uint amount, uint bucket) public { - amount = bound(amount, 1, 2 ** 64); - vm.assume(bucket < N); - - borrowableAsset.setBalance(address(this), amount); - market.modifyDeposit(int(amount), bucket); - - assertEq(market.supplyShare(address(this), bucket), 1e18); - assertEq(borrowableAsset.balanceOf(address(this)), 0); - assertEq(borrowableAsset.balanceOf(address(market)), amount); - } - - function testBorrow(uint amountLent, uint amountBorrowed, uint bucket) public { - amountLent = bound(amountLent, 0, 2 ** 64); - amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); - vm.assume(bucket < N); - - borrowableAsset.setBalance(address(this), amountLent); - market.modifyDeposit(int(amountLent), bucket); - - if (amountBorrowed == 0) { - market.modifyBorrow(int(amountBorrowed), bucket); - return; - } - - if (amountBorrowed > amountLent) { - vm.prank(borrower); - vm.expectRevert("not enough liquidity"); - market.modifyBorrow(int(amountBorrowed), bucket); - return; - } - - vm.prank(borrower); - market.modifyBorrow(int(amountBorrowed), bucket); - - assertEq(market.borrowShare(borrower, bucket), 1e18); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed); - assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed); - } - - function testWithdraw(uint amountLent, uint amountWithdrawn, uint amountBorrowed, uint bucket) public { - amountLent = bound(amountLent, 1, 2 ** 64); - vm.assume(bucket < N); - vm.assume(amountLent >= amountBorrowed); - vm.assume(int(amountWithdrawn) >= 0); - - borrowableAsset.setBalance(address(this), amountLent); - market.modifyDeposit(int(amountLent), bucket); - - vm.prank(borrower); - market.modifyBorrow(int(amountBorrowed), bucket); - - if (amountWithdrawn > amountLent - amountBorrowed) { - if (amountWithdrawn > amountLent) { - vm.expectRevert(); - } else { - vm.expectRevert("not enough liquidity"); - } - market.modifyDeposit(-int(amountWithdrawn), bucket); - return; - } - - market.modifyDeposit(-int(amountWithdrawn), bucket); - - assertApproxEqAbs( - market.supplyShare(address(this), bucket), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3 - ); - assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn); - assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed - amountWithdrawn); - } - - function testCollateralRequirements( - uint amountCollateral, - uint amountBorrowed, - uint priceCollateral, - uint priceBorrowable, - uint bucket - ) public { - amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); - priceBorrowable = bound(priceBorrowable, 0, 2 ** 64); - amountCollateral = bound(amountCollateral, 0, 2 ** 64); - priceCollateral = bound(priceCollateral, 0, 2 ** 64); - vm.assume(bucket < N); - - borrowableOracle.setPrice(priceBorrowable); - collateralOracle.setPrice(priceCollateral); - - borrowableAsset.setBalance(address(this), amountBorrowed); - collateralAsset.setBalance(borrower, amountCollateral); - - market.modifyDeposit(int(amountBorrowed), bucket); - - vm.prank(borrower); - market.modifyCollateral(int(amountCollateral), bucket); - - uint collateralValue = amountCollateral.wMul(priceCollateral); - uint borrowValue = amountBorrowed.wMul(priceBorrowable); - if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(bucketToLLTV(bucket)))) { - vm.prank(borrower); - market.modifyBorrow(int(amountBorrowed), bucket); - } else { - vm.prank(borrower); - vm.expectRevert("not enough collateral"); - market.modifyBorrow(int(amountBorrowed), bucket); - } - } - - function testRepay(uint amountLent, uint amountBorrowed, uint amountRepaid, uint bucket) public { - amountLent = bound(amountLent, 1, 2 ** 64); - amountBorrowed = bound(amountBorrowed, 1, amountLent); - amountRepaid = bound(amountRepaid, 0, amountBorrowed); - vm.assume(bucket < N); - - borrowableAsset.setBalance(address(this), amountLent); - market.modifyDeposit(int(amountLent), bucket); - - vm.startPrank(borrower); - market.modifyBorrow(int(amountBorrowed), bucket); - market.modifyBorrow(-int(amountRepaid), bucket); - vm.stopPrank(); - - assertApproxEqAbs( - market.borrowShare(borrower, bucket), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3 - ); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid); - assertEq(borrowableAsset.balanceOf(address(market)), amountLent - amountBorrowed + amountRepaid); - } - - function testDepositCollateral(uint amount, uint bucket) public { - vm.assume(bucket < N); - vm.assume(int(amount) >= 0); - - collateralAsset.setBalance(address(this), amount); - market.modifyCollateral(int(amount), bucket); - - assertEq(market.collateral(address(this), bucket), amount); - assertEq(collateralAsset.balanceOf(address(this)), 0); - assertEq(collateralAsset.balanceOf(address(market)), amount); - } - - function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn, uint bucket) public { - vm.assume(bucket < N); - vm.assume(int(amountDeposited) > 0); - vm.assume(int(amountWithdrawn) > 0); - - collateralAsset.setBalance(address(this), amountDeposited); - market.modifyCollateral(int(amountDeposited), bucket); - - if (amountWithdrawn > amountDeposited) { - vm.expectRevert("negative"); - market.modifyCollateral(-int(amountWithdrawn), bucket); - return; - } - - market.modifyCollateral(-int(amountWithdrawn), bucket); - - assertEq(market.collateral(address(this), bucket), amountDeposited - amountWithdrawn); - assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn); - assertEq(collateralAsset.balanceOf(address(market)), amountDeposited - amountWithdrawn); - } - - function testTwoUsersSupply(uint firstAmount, uint secondAmount, uint bucket) public { - vm.assume(bucket < N); - firstAmount = bound(firstAmount, 1, 2 ** 64); - secondAmount = bound(secondAmount, 0, 2 ** 64); - - borrowableAsset.setBalance(address(this), firstAmount); - market.modifyDeposit(int(firstAmount), bucket); - - borrowableAsset.setBalance(borrower, secondAmount); - vm.prank(borrower); - market.modifyDeposit(int(secondAmount), bucket); - - assertEq(market.supplyShare(address(this), bucket), 1e18); - assertEq(market.supplyShare(borrower, bucket), secondAmount * 1e18 / firstAmount); - } - - function testModifyDepositUnknownBucket(uint bucket) public { - vm.assume(bucket > N); - vm.expectRevert("unknown bucket"); - market.modifyDeposit(1, bucket); - } - - function testModifyBorrowUnknownBucket(uint bucket) public { - vm.assume(bucket > N); - vm.expectRevert("unknown bucket"); - market.modifyBorrow(1, bucket); - } - - function testModifyCollateralUnknownBucket(uint bucket) public { - vm.assume(bucket > N); - vm.expectRevert("unknown bucket"); - market.modifyCollateral(1, bucket); - } - - function testWithdrawEmptyMarket(int amount, uint bucket) public { - vm.assume(bucket < N); - vm.assume(amount < 0); - vm.expectRevert(stdError.divisionError); - market.modifyDeposit(amount, bucket); - } - - function testRepayEmptyMarket(int amount, uint bucket) public { - vm.assume(bucket < N); - vm.assume(amount < 0); - vm.expectRevert(stdError.divisionError); - market.modifyBorrow(amount, bucket); - } - - function testWithdrawCollateralEmptyMarket(int amount, uint bucket) public { - vm.assume(bucket < N); - vm.assume(amount < 0); - vm.expectRevert("negative"); - market.modifyCollateral(amount, bucket); - } -} diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 3077e126f..e82b1ba79 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -1,18 +1,18 @@ -import { BigNumber, Wallet, constants } from "ethers"; -import hre from "hardhat"; - import { hexZeroPad } from "@ethersproject/bytes"; +import { keccak256 } from "@ethersproject/keccak256"; +import { toUtf8Bytes } from "@ethersproject/strings"; import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; - -import { Market, OracleMock, ERC20Mock } from "types"; +import { BigNumber, Wallet, constants } from "ethers"; +import hre from "hardhat"; +import { Blue, OracleMock, ERC20Mock } from "types"; const iterations = 500; let seed = 42; function next() { - seed = seed * 16807 % 2147483647; + seed = (seed * 16807) % 2147483647; return seed; } @@ -20,70 +20,99 @@ function random() { return (next() - 1) / 2147483646; } -describe("Market", () => { +interface Info { + borrowableAsset: string; + collateralAsset: string; + borrowableOracle: string; + collateralOracle: string; + lLTV: BigNumber; +} + +describe("Blue", () => { let signers: SignerWithAddress[]; + let blue: Blue; let borrowable: ERC20Mock; let collateral: ERC20Mock; let borrowableOracle: OracleMock; let collateralOracle: OracleMock; - let market: Market; + let info: Info; + let id: Buffer; const initBalance = constants.MaxUint256.div(2); beforeEach(async () => { signers = await hre.ethers.getSigners(); - const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", signers[0]); + const ERC20MockFactory = await hre.ethers.getContractFactory( + "ERC20Mock", + signers[0] + ); borrowable = await ERC20MockFactory.deploy("DAI", "DAI", 18); collateral = await ERC20MockFactory.deploy("Wrapped BTC", "WBTC", 18); - const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", signers[0]); + const OracleMockFactory = await hre.ethers.getContractFactory( + "OracleMock", + signers[0] + ); borrowableOracle = await OracleMockFactory.deploy(); collateralOracle = await OracleMockFactory.deploy(); - const MarketFactory = await hre.ethers.getContractFactory("Market", signers[0]); + const BlueFactory = await hre.ethers.getContractFactory("Blue", signers[0]); - market = await MarketFactory.deploy( - borrowable.address, - collateral.address, - borrowableOracle.address, - collateralOracle.address - ); + blue = await BlueFactory.deploy(); + + info = { + borrowableAsset: borrowable.address, + collateralAsset: collateral.address, + borrowableOracle: borrowableOracle.address, + collateralOracle: collateralOracle.address, + lLTV: BigNumber.WAD, + }; + + const encodedInfo = toUtf8Bytes(JSON.stringify(info)); + const hashedInfoHex = keccak256(encodedInfo); + id = Buffer.from(hashedInfoHex.slice(2), "hex"); + + await blue.connect(signers[0]).createMarket(info); }); it("should simulate gas cost", async () => { - const n = (await market.getN()).toNumber(); - for (let i = 1; i < iterations; ++i) { console.log(i, "/", iterations); - const user = new Wallet(hexZeroPad(BigNumber.from(i).toHexString(), 32), hre.ethers.provider); + const user = new Wallet( + hexZeroPad(BigNumber.from(i).toHexString(), 32), + hre.ethers.provider + ); await setBalance(user.address, initBalance); await borrowable.setBalance(user.address, initBalance); - await borrowable.connect(user).approve(market.address, constants.MaxUint256); + await borrowable + .connect(user) + .approve(blue.address, constants.MaxUint256); await collateral.setBalance(user.address, initBalance); - await collateral.connect(user).approve(market.address, constants.MaxUint256); + await collateral + .connect(user) + .approve(blue.address, constants.MaxUint256); let amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); - const bucket = Math.floor(random() * n); let supplyOnly: boolean = random() < 2 / 3; if (supplyOnly) { - await market.connect(user).modifyDeposit(amount, bucket); - await market.connect(user).modifyDeposit(amount.div(2).mul(-1), bucket); + await blue.connect(user).modifyDeposit(info, amount); + await blue.connect(user).modifyDeposit(info, amount.div(2).mul(-1)); } else { - const totalSupply = await market.totalSupply(bucket); - const totalBorrow = await market.totalBorrow(bucket); + const totalSupply = await blue.totalSupply(id); + const totalBorrow = await blue.totalBorrow(id); let liq = BigNumber.from(totalSupply).sub(BigNumber.from(totalBorrow)); amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); - await market.connect(user).modifyCollateral(amount, bucket); - await market.connect(user).modifyBorrow(amount.div(2), bucket); - await market.connect(user).modifyBorrow(amount.div(4).mul(-1), bucket); - await market.connect(user).modifyCollateral(amount.div(8).mul(-1), bucket); + await blue.connect(user).modifyCollateral(info, amount); + await blue.connect(user).modifyBorrow(info, amount.div(2)); + await blue.connect(user).modifyBorrow(info, amount.div(4).mul(-1)); + await blue.connect(user).modifyCollateral(info, amount.div(8).mul(-1)); } } }); From 42e4ea07e21adf44124ad03d53d13eb67b6ead30 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 14:31:55 +0200 Subject: [PATCH 020/112] feat: estimate gas cost of batch liquidation --- src/Market.sol | 8 +- test/hardhat/Market.spec.ts | 58 ++++++----- yarn.lock | 195 +++++++++++++++++++----------------- 3 files changed, 143 insertions(+), 118 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index e513c3b99..839a4c1b6 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -15,10 +15,6 @@ uint constant alpha = 0.5e18; uint constant N = 10; -function bucketToLLTV(uint bucket) pure returns (uint) { - return MathLib.wDiv(bucket + 1, N + 1); -} - function irm(uint utilization) pure returns (uint) { // Divide by the number of seconds in a year. // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. @@ -30,6 +26,10 @@ contract Market { using MathLib for uint; using SafeTransferLib for IERC20; + function bucketToLLTV(uint bucket) public pure returns (uint) { + return MathLib.wDiv(bucket + 1, N + 1); + } + // Constants. uint public constant getN = N; diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 3077e126f..c8c4d1063 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -1,13 +1,12 @@ -import { BigNumber, Wallet, constants } from "ethers"; +import { BigNumber, constants } from "ethers"; import hre from "hardhat"; -import { hexZeroPad } from "@ethersproject/bytes"; import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Market, OracleMock, ERC20Mock } from "types"; -const iterations = 500; +let nbLiquidations = 3; let seed = 42; @@ -20,6 +19,12 @@ function random() { return (next() - 1) / 2147483646; } +function assert(condition: boolean, message: string) { + if (!condition) { + throw message || "Assertion failed"; + } +} + describe("Market", () => { let signers: SignerWithAddress[]; @@ -37,13 +42,16 @@ describe("Market", () => { const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", signers[0]); borrowable = await ERC20MockFactory.deploy("DAI", "DAI", 18); - collateral = await ERC20MockFactory.deploy("Wrapped BTC", "WBTC", 18); + collateral = await ERC20MockFactory.deploy("USDC", "USDC", 18); const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", signers[0]); borrowableOracle = await OracleMockFactory.deploy(); collateralOracle = await OracleMockFactory.deploy(); + await borrowableOracle.connect(signers[0]).setPrice("1000000000000000000"); + await collateralOracle.connect(signers[0]).setPrice("1000000000000000000"); + const MarketFactory = await hre.ethers.getContractFactory("Market", signers[0]); market = await MarketFactory.deploy( @@ -56,11 +64,15 @@ describe("Market", () => { it("should simulate gas cost", async () => { const n = (await market.getN()).toNumber(); + assert(nbLiquidations < n, "more liquidations than buckets"); + assert(nbLiquidations < 20, "more liquidations than signers"); - for (let i = 1; i < iterations; ++i) { - console.log(i, "/", iterations); + let liquidationData = [] - const user = new Wallet(hexZeroPad(BigNumber.from(i).toHexString(), 32), hre.ethers.provider); + // Create accounts close to liquidation + for (let i = 0; i < nbLiquidations; ++i) { + const user = signers[i]; + const bucket = i; await setBalance(user.address, initBalance); await borrowable.setBalance(user.address, initBalance); await borrowable.connect(user).approve(market.address, constants.MaxUint256); @@ -68,23 +80,21 @@ describe("Market", () => { await collateral.connect(user).approve(market.address, constants.MaxUint256); let amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); - const bucket = Math.floor(random() * n); - - let supplyOnly: boolean = random() < 2 / 3; - if (supplyOnly) { - await market.connect(user).modifyDeposit(amount, bucket); - await market.connect(user).modifyDeposit(amount.div(2).mul(-1), bucket); - } else { - const totalSupply = await market.totalSupply(bucket); - const totalBorrow = await market.totalBorrow(bucket); - let liq = BigNumber.from(totalSupply).sub(BigNumber.from(totalBorrow)); - amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); - - await market.connect(user).modifyCollateral(amount, bucket); - await market.connect(user).modifyBorrow(amount.div(2), bucket); - await market.connect(user).modifyBorrow(amount.div(4).mul(-1), bucket); - await market.connect(user).modifyCollateral(amount.div(8).mul(-1), bucket); - } + + await market.connect(user).modifyDeposit(amount, bucket); + await market.connect(user).modifyCollateral(amount, bucket); + + let lltv = await market.bucketToLLTV(bucket); + let borrowedAmount = amount.mul(lltv).div(BigNumber.WAD); + await market.connect(user).modifyBorrow(borrowedAmount, bucket); + + let maxCollat = borrowedAmount.div(1000); + + liquidationData.push({ bucket: bucket, borrower: user.address, maxCollat: maxCollat }); } + + await borrowableOracle.connect(signers[0]).setPrice("2000000000000000000"); + + await market.connect(signers[0]).batchLiquidate(liquidationData); }); }); diff --git a/yarn.lock b/yarn.lock index 88f75030b..652b4b9a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" - integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== +"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: - "@babel/highlight" "^7.22.5" + "@babel/highlight" "^7.18.6" "@babel/generator@7.17.7": version "7.17.7" @@ -19,74 +19,74 @@ source-map "^0.5.0" "@babel/generator@^7.17.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" - integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" + integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.3" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-environment-visitor@^7.16.7": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" + integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== "@babel/helper-function-name@^7.16.7": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.16.7": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.18.6" "@babel/helper-split-export-declaration@^7.16.7": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" - integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== -"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.17.3", "@babel/parser@^7.20.5", "@babel/parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" - integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== +"@babel/parser@^7.17.3", "@babel/parser@^7.20.5", "@babel/parser@^7.21.9": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" + integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== -"@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== +"@babel/template@^7.20.7": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" + integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/code-frame" "^7.21.4" + "@babel/parser" "^7.21.9" + "@babel/types" "^7.21.5" "@babel/traverse@7.17.3": version "7.17.3" @@ -112,13 +112,13 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.17.0", "@babel/types@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" - integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== +"@babel/types@^7.17.0", "@babel/types@^7.18.6", "@babel/types@^7.21.0", "@babel/types@^7.21.5", "@babel/types@^7.22.3": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" + integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@chainsafe/as-sha256@^0.3.1": @@ -567,6 +567,14 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" +"@morgan-stanley/ts-mocking-bird@^0.6.2": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz#2e4b60d42957bab3b50b67dbf14c3da2f62a39f7" + integrity sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA== + dependencies: + lodash "^4.17.16" + uuid "^7.0.3" + "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" @@ -1041,9 +1049,9 @@ integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== "@types/node@*", "@types/node@^20.2.5": - version "20.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" - integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + version "20.2.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" + integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== "@types/node@^10.0.3": version "10.17.60" @@ -1063,9 +1071,9 @@ "@types/node" "*" "@types/prettier@^2.1.1": - version "2.7.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" - integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== "@types/qs@^6.2.31": version "6.9.7" @@ -1123,9 +1131,9 @@ acorn-walk@^8.1.1: integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^8.4.1: - version "8.9.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" - integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== address@^1.0.1: version "1.2.2" @@ -1975,9 +1983,9 @@ dir-glob@^3.0.1: path-type "^4.0.0" dotenv@^16.1.3: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + version "16.1.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.1.3.tgz#0c67e90d0ddb48d08c570888f709b41844928210" + integrity sha512-FYssxsmCTtKL72fGBSvb1K9dRz0/VZeWqFme/vSb7r7323x4CRaHu4LvQ5JG3+s6yt2YPbBrkpiEODktfyjI9A== eastasianwidth@^0.2.0: version "0.2.0" @@ -2764,9 +2772,9 @@ har-validator@~5.1.3: har-schema "^2.0.0" hardhat-deal@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/hardhat-deal/-/hardhat-deal-1.4.0.tgz#3f42f52603c1f9eeb7e583ca7261478d17b875a4" - integrity sha512-8+p0c7u3RsRWf5mzAAUbJTq11HAJJb+tj6AcM9w4ObN1LTMBouQ7D6sc3zQwLP8dddYbA7z0wrkp+zn61cPxCg== + version "1.3.0" + resolved "https://registry.yarnpkg.com/hardhat-deal/-/hardhat-deal-1.3.0.tgz#06fbbaafc78e3dce07159049064bc1cb551ca014" + integrity sha512-jnD2nryxA+GSZ4RsuKAoIQYhGK1+U0gjMS4SZdCz5Wxmfdv4Qgk05lqQxsR6Ko5nMG27uMJAWPVHjYdvhaRH4w== hardhat-gas-reporter@^1.0.9: version "1.0.9" @@ -2786,9 +2794,9 @@ hardhat-tracer@2.3.2: ethers "^5.6.1" hardhat@^2.14.1: - version "2.16.0" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.16.0.tgz#c5611d433416b31f6ce92f733b1f1b5236ad6230" - integrity sha512-7VQEJPQRAZdtrYUZaU9GgCpP3MBNy/pTdscARNJQMWKj5C+R7V32G5uIZKIqZ4QiqXa6CBfxxe+G+ahxUbHZHA== + version "2.14.1" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.14.1.tgz#4dd252717f4987d8221c4f6fd08233b7f4251fd8" + integrity sha512-H3Qp/UKyQGmPDDBSfMoSyH18rRnac90rsb0LNer+sKe6at6rxLe4D5j+M+1icqZQF02iLPjNRwc/PA8OPf757A== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -2829,6 +2837,7 @@ hardhat@^2.14.1: mnemonist "^0.38.0" mocha "^10.0.0" p-map "^4.0.0" + qs "^6.7.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" @@ -3253,9 +3262,9 @@ javascript-natural-sort@0.7.1: integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== js-sdsl@^4.1.4: - version "4.4.1" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.1.tgz#9e3c7b566d8d9a7e1fe8fc26d00b5ab0f8918ab3" - integrity sha512-6Gsx8R0RucyePbWqPssR8DyfuXmLBooYN5cZFZKjHGnQuaf7pEzhtpceagJxVu4LqhYY5EYA7nko3FmeHZ1KbA== + version "4.4.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== js-sha3@0.5.7: version "0.5.7" @@ -3474,7 +3483,7 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4115,7 +4124,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -qs@^6.4.0: +qs@^6.4.0, qs@^6.7.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -4460,9 +4469,9 @@ semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.4: - version "7.5.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" - integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== + version "7.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" + integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== dependencies: lru-cache "^6.0.0" @@ -4591,9 +4600,9 @@ solc@0.7.3: tmp "0.0.33" solidity-coverage@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.3.tgz#72ce51e5ca9ea1182bbf6085eb1cf526f0603b52" - integrity sha512-hbcNgj5z8zzgTlnp4F0pXiqj1v5ua8P4DH5i9cWOBtFPfUuIohLoXu5WiAixexWmpKVjyxXqupnu/mPb4IGr7Q== + version "0.8.2" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.2.tgz#bc39604ab7ce0a3fa7767b126b44191830c07813" + integrity sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ== dependencies: "@ethersproject/abi" "^5.0.9" "@solidity-parser/parser" "^0.14.1" @@ -4940,10 +4949,11 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: punycode "^2.1.1" 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" - integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== + version "2.5.0" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz#7eeed3a6937b2612ea08a0794cf9d43fbbea89c4" + integrity sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw== dependencies: + "@morgan-stanley/ts-mocking-bird" "^0.6.2" chalk "^4.1.0" command-line-args "^5.1.1" command-line-usage "^6.1.0" @@ -4979,9 +4989,9 @@ tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + version "2.5.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" + integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== tsort@0.0.1: version "0.0.1" @@ -5141,6 +5151,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" From 58687e5cc17ad464b7e4d0f4ba86ce821e4ba8e5 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 15:28:33 +0200 Subject: [PATCH 021/112] test: compare batching with naive solution --- src/Market.sol | 14 +++++++++++ test/hardhat/Market.spec.ts | 50 ++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 839a4c1b6..05731a911 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -163,6 +163,20 @@ contract Market { IERC20(borrowableAsset).handleTransfer(msg.sender, -sumBorrow); } + function naiveBatchLiquidate(Liquidation[] memory liquidationData) + external + returns (int sumCollat, int sumBorrow) + { + for (uint i; i < liquidationData.length; i++) { + Liquidation memory liq = liquidationData[i]; + (int collat, int borrow) = liquidate(liq.bucket, liq.borrower, liq.maxCollat); + IERC20(collateralAsset).handleTransfer(msg.sender, collat); + IERC20(borrowableAsset).handleTransfer(msg.sender, -borrow); + sumCollat += collat; + sumBorrow += borrow; + } + } + /// @return collat The negative amount of collateral added. /// @return borrow The negative amount of borrow added. function liquidate(uint bucket, address borrower, uint maxCollat) internal returns (int collat, int borrow) { diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index c8c4d1063..44919bde6 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -6,7 +6,11 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Market, OracleMock, ERC20Mock } from "types"; -let nbLiquidations = 3; +function assert(condition: boolean, message: string) { + if (!condition) { + throw message || "Assertion failed"; + } +} let seed = 42; @@ -19,11 +23,11 @@ function random() { return (next() - 1) / 2147483646; } -function assert(condition: boolean, message: string) { - if (!condition) { - throw message || "Assertion failed"; - } -} +let nbLiquidations = 5; +let naiveBatching = true; +let closePositions = false; + +assert(nbLiquidations < 20, "more liquidations than signers"); describe("Market", () => { let signers: SignerWithAddress[]; @@ -33,24 +37,28 @@ describe("Market", () => { let borrowableOracle: OracleMock; let collateralOracle: OracleMock; let market: Market; + let admin: SignerWithAddress; + let liquidator: SignerWithAddress; const initBalance = constants.MaxUint256.div(2); beforeEach(async () => { signers = await hre.ethers.getSigners(); + admin = signers[nbLiquidations]; + liquidator = signers[nbLiquidations + 1]; const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", signers[0]); borrowable = await ERC20MockFactory.deploy("DAI", "DAI", 18); collateral = await ERC20MockFactory.deploy("USDC", "USDC", 18); - const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", signers[0]); + const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", admin); borrowableOracle = await OracleMockFactory.deploy(); collateralOracle = await OracleMockFactory.deploy(); - await borrowableOracle.connect(signers[0]).setPrice("1000000000000000000"); - await collateralOracle.connect(signers[0]).setPrice("1000000000000000000"); + await borrowableOracle.connect(admin).setPrice(BigNumber.WAD); + await collateralOracle.connect(admin).setPrice(BigNumber.WAD); const MarketFactory = await hre.ethers.getContractFactory("Market", signers[0]); @@ -64,9 +72,7 @@ describe("Market", () => { it("should simulate gas cost", async () => { const n = (await market.getN()).toNumber(); - assert(nbLiquidations < n, "more liquidations than buckets"); - assert(nbLiquidations < 20, "more liquidations than signers"); - + assert(nbLiquidations <= n, "more liquidations than buckets"); let liquidationData = [] // Create accounts close to liquidation @@ -88,13 +94,27 @@ describe("Market", () => { let borrowedAmount = amount.mul(lltv).div(BigNumber.WAD); await market.connect(user).modifyBorrow(borrowedAmount, bucket); - let maxCollat = borrowedAmount.div(1000); + let maxCollat = closePositions ? constants.MaxUint256 : amount.div(2); liquidationData.push({ bucket: bucket, borrower: user.address, maxCollat: maxCollat }); } - await borrowableOracle.connect(signers[0]).setPrice("2000000000000000000"); + await borrowableOracle.connect(admin).setPrice(BigNumber.WAD.mul(1000)); + + await setBalance(liquidator.address, initBalance); + await borrowable.setBalance(liquidator.address, initBalance); + await borrowable.connect(liquidator).approve(market.address, constants.MaxUint256); + if (naiveBatching) { + await market.connect(liquidator).naiveBatchLiquidate(liquidationData); + } else { + await market.connect(liquidator).batchLiquidate(liquidationData); + } - await market.connect(signers[0]).batchLiquidate(liquidationData); + for (let i = 0; i < nbLiquidations; i++) { + const user = signers[i]; + const bucket = i; + let collat = await market.collateral(user.address, bucket); + assert(closePositions || collat != BigNumber.from(0), "did not take the whole collateral when closing the position"); + } }); }); From 93b466124ea5123b1ed60515f7d7c2d903ad1ae0 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 16:52:40 +0200 Subject: [PATCH 022/112] feat: realize bad debt --- src/Market.sol | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index e513c3b99..448048206 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -179,12 +179,23 @@ contract Market { // Safe to cast because it's smaller than collateral[borrower][bucket] collat = -int(maxCollat.min(collateral[borrower][bucket])); borrow = collat.wMul(collatPrice).wDiv(incentive).wDiv(borrowPrice); - int shares = borrow.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); - borrowShare[borrower][bucket] = (int(borrowShare[borrower][bucket]) + shares).safeToUint(); - totalBorrowShares[bucket] = (int(totalBorrowShares[bucket]) + shares).safeToUint(); + + uint priorBorrowShares = borrowShare[borrower][bucket]; + // Limit the liquidation to the debt of the borrower. + uint newBorrowShares = (int(priorBorrowShares) + shares).safeToUint(); + uint newCollateral = (int(collateral[borrower][bucket]) + collat).safeToUint(); + totalBorrow[bucket] = (int(totalBorrow[bucket]) + borrow).safeToUint(); - collateral[borrower][bucket] = (int(collateral[borrower][bucket]) + collat).safeToUint(); + if (newCollateral == 0) { + // Realize the bad debt. + totalBorrowShares[bucket] -= priorBorrowShares; + borrowShare[borrower][bucket] = 0; + } else { + totalBorrowShares[bucket] = (int(totalBorrowShares[bucket]) + shares).safeToUint(); + borrowShare[borrower][bucket] = newBorrowShares; + } + collateral[borrower][bucket] = newCollateral; } // Interests management. From f792083936ef51c2d186f32e1acf755a3d312f9a Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 17:18:59 +0200 Subject: [PATCH 023/112] test: add liquidator contract mock --- src/Market.sol | 16 ++++------------ src/mocks/LiquidatorMock.sol | 30 ++++++++++++++++++++++++++++++ test/hardhat/Market.spec.ts | 23 ++++++++++++++--------- 3 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 src/mocks/LiquidatorMock.sol diff --git a/src/Market.sol b/src/Market.sol index 659991dea..4ceb6670f 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -163,18 +163,10 @@ contract Market { IERC20(borrowableAsset).handleTransfer(msg.sender, -sumBorrow); } - function naiveBatchLiquidate(Liquidation[] memory liquidationData) - external - returns (int sumCollat, int sumBorrow) - { - for (uint i; i < liquidationData.length; i++) { - Liquidation memory liq = liquidationData[i]; - (int collat, int borrow) = liquidate(liq.bucket, liq.borrower, liq.maxCollat); - IERC20(collateralAsset).handleTransfer(msg.sender, collat); - IERC20(borrowableAsset).handleTransfer(msg.sender, -borrow); - sumCollat += collat; - sumBorrow += borrow; - } + function singleLiquidate(uint bucket, address borrower, uint maxCollat) external returns (int collat, int borrow) { + (collat, borrow) = liquidate(bucket, borrower, maxCollat); + IERC20(collateralAsset).handleTransfer(msg.sender, collat); + IERC20(borrowableAsset).handleTransfer(msg.sender, -borrow); } /// @return collat The negative amount of collateral added. diff --git a/src/mocks/LiquidatorMock.sol b/src/mocks/LiquidatorMock.sol new file mode 100644 index 000000000..742fdd1ea --- /dev/null +++ b/src/mocks/LiquidatorMock.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {ERC20Mock} from "./ERC20Mock.sol"; +import {Market} from "src/Market.sol"; + +contract LiquidatorMock { + function approveBorrowable(Market market) public { + ERC20Mock(market.borrowableAsset()).approve(address(market), type(uint).max); + } + + function nativeBatchLiquidate(Market market, Market.Liquidation[] memory liquidationData) + external + returns (int sumCollat, int sumBorrow) + { + return market.batchLiquidate(liquidationData); + } + + function manualBatchLiquidate(Market market, Market.Liquidation[] memory liquidationData) + external + returns (int sumCollat, int sumBorrow) + { + for (uint i; i < liquidationData.length; i++) { + Market.Liquidation memory liq = liquidationData[i]; + (int collat, int borrow) = market.singleLiquidate(liq.bucket, liq.borrower, liq.maxCollat); + sumCollat += collat; + sumBorrow += borrow; + } + } +} diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 44919bde6..53882be03 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -4,7 +4,7 @@ import hre from "hardhat"; import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { Market, OracleMock, ERC20Mock } from "types"; +import { Market, OracleMock, ERC20Mock, LiquidatorMock } from "types"; function assert(condition: boolean, message: string) { if (!condition) { @@ -24,7 +24,7 @@ function random() { } let nbLiquidations = 5; -let naiveBatching = true; +let nativeBatching = false; let closePositions = false; assert(nbLiquidations < 20, "more liquidations than signers"); @@ -37,6 +37,7 @@ describe("Market", () => { let borrowableOracle: OracleMock; let collateralOracle: OracleMock; let market: Market; + let liquidatorContract: LiquidatorMock; let admin: SignerWithAddress; let liquidator: SignerWithAddress; @@ -47,7 +48,7 @@ describe("Market", () => { admin = signers[nbLiquidations]; liquidator = signers[nbLiquidations + 1]; - const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", signers[0]); + const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", admin); borrowable = await ERC20MockFactory.deploy("DAI", "DAI", 18); collateral = await ERC20MockFactory.deploy("USDC", "USDC", 18); @@ -60,7 +61,7 @@ describe("Market", () => { await borrowableOracle.connect(admin).setPrice(BigNumber.WAD); await collateralOracle.connect(admin).setPrice(BigNumber.WAD); - const MarketFactory = await hre.ethers.getContractFactory("Market", signers[0]); + const MarketFactory = await hre.ethers.getContractFactory("Market", admin); market = await MarketFactory.deploy( borrowable.address, @@ -68,6 +69,10 @@ describe("Market", () => { borrowableOracle.address, collateralOracle.address ); + + const LiquidatorMockFactory = await hre.ethers.getContractFactory("LiquidatorMock", admin); + + liquidatorContract = await LiquidatorMockFactory.deploy(); }); it("should simulate gas cost", async () => { @@ -102,12 +107,12 @@ describe("Market", () => { await borrowableOracle.connect(admin).setPrice(BigNumber.WAD.mul(1000)); await setBalance(liquidator.address, initBalance); - await borrowable.setBalance(liquidator.address, initBalance); - await borrowable.connect(liquidator).approve(market.address, constants.MaxUint256); - if (naiveBatching) { - await market.connect(liquidator).naiveBatchLiquidate(liquidationData); + await borrowable.setBalance(liquidatorContract.address, initBalance); + await liquidatorContract.connect(liquidator).approveBorrowable(market.address); + if (nativeBatching) { + await liquidatorContract.connect(liquidator).nativeBatchLiquidate(market.address, liquidationData); } else { - await market.connect(liquidator).batchLiquidate(liquidationData); + await liquidatorContract.connect(liquidator).manualBatchLiquidate(market.address, liquidationData); } for (let i = 0; i < nbLiquidations; i++) { From d5b0fae0e0550eea33de958a51dd4d736626c1b6 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 17:22:10 +0200 Subject: [PATCH 024/112] fix: bucketToLLTV in contract --- test/forge/Market.t.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index e7df1e7b0..837625691 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -181,7 +181,10 @@ contract MarketTest is Test { uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); - if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(bucketToLLTV(bucket)))) { + if ( + borrowValue == 0 + || (collateralValue > 0 && borrowValue <= collateralValue.wMul(market.bucketToLLTV(bucket))) + ) { vm.prank(borrower); market.modifyBorrow(int(amountBorrowed), bucket); } else { @@ -251,7 +254,7 @@ contract MarketTest is Test { vm.assume(bucket < N); uint amountCollateral = amountLent; - uint lLTV = bucketToLLTV(bucket); + uint lLTV = market.bucketToLLTV(bucket); uint borrowingPower = amountCollateral.wMul(lLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); uint maxCollat = amountCollateral.wMul(lLTV); From 9a42a7f802fa21fb0ec058da003ed19d072d1c34 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 28 Jun 2023 18:20:59 +0200 Subject: [PATCH 025/112] fix: bucket with only one borrower --- test/hardhat/Market.spec.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 53882be03..12b23a31a 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -27,7 +27,7 @@ let nbLiquidations = 5; let nativeBatching = false; let closePositions = false; -assert(nbLiquidations < 20, "more liquidations than signers"); +assert(2 * nbLiquidations + 1 < 20, "more liquidations than signers"); describe("Market", () => { let signers: SignerWithAddress[]; @@ -45,8 +45,8 @@ describe("Market", () => { beforeEach(async () => { signers = await hre.ethers.getSigners(); - admin = signers[nbLiquidations]; - liquidator = signers[nbLiquidations + 1]; + admin = signers[2 * nbLiquidations]; + liquidator = signers[2 * nbLiquidations + 1]; const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", admin); @@ -77,13 +77,13 @@ describe("Market", () => { it("should simulate gas cost", async () => { const n = (await market.getN()).toNumber(); - assert(nbLiquidations <= n, "more liquidations than buckets"); + assert(2 * nbLiquidations <= n, "more liquidations than buckets"); let liquidationData = [] // Create accounts close to liquidation - for (let i = 0; i < nbLiquidations; ++i) { + for (let i = 0; i < 2 * nbLiquidations; ++i) { const user = signers[i]; - const bucket = i; + const bucket = Math.floor(i / 2); await setBalance(user.address, initBalance); await borrowable.setBalance(user.address, initBalance); await borrowable.connect(user).approve(market.address, constants.MaxUint256); @@ -101,7 +101,9 @@ describe("Market", () => { let maxCollat = closePositions ? constants.MaxUint256 : amount.div(2); - liquidationData.push({ bucket: bucket, borrower: user.address, maxCollat: maxCollat }); + if (i % 2 == 0) { + liquidationData.push({ bucket: bucket, borrower: user.address, maxCollat: maxCollat }); + } } await borrowableOracle.connect(admin).setPrice(BigNumber.WAD.mul(1000)); @@ -115,9 +117,9 @@ describe("Market", () => { await liquidatorContract.connect(liquidator).manualBatchLiquidate(market.address, liquidationData); } - for (let i = 0; i < nbLiquidations; i++) { + for (let i = 0; i < 2 * nbLiquidations; i++) { const user = signers[i]; - const bucket = i; + const bucket = Math.floor(i / 2); let collat = await market.collateral(user.address, bucket); assert(closePositions || collat != BigNumber.from(0), "did not take the whole collateral when closing the position"); } From 0e2a8e3341616646cc5d24c43334370732985f14 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Thu, 29 Jun 2023 12:15:08 +0200 Subject: [PATCH 026/112] feat: allow max liquidations --- src/Market.sol | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 448048206..8ae274d1e 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -7,8 +7,6 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; -import "forge-std/console.sol"; - uint constant WAD = 1e18; uint constant alpha = 0.5e18; @@ -179,21 +177,28 @@ contract Market { // Safe to cast because it's smaller than collateral[borrower][bucket] collat = -int(maxCollat.min(collateral[borrower][bucket])); borrow = collat.wMul(collatPrice).wDiv(incentive).wDiv(borrowPrice); + uint priorBorrowShares = borrowShare[borrower][bucket]; + uint priorBorrow = priorBorrowShares.wMul(totalBorrow[bucket]).wDiv(totalBorrowShares[bucket]); + if (int(priorBorrow) + borrow < 0) { + borrow = -int(priorBorrow); + collat = borrow.wDiv(collatPrice).wMul(incentive).wMul(borrowPrice); + } int shares = borrow.wMul(totalBorrowShares[bucket]).wDiv(totalBorrow[bucket]); - uint priorBorrowShares = borrowShare[borrower][bucket]; - // Limit the liquidation to the debt of the borrower. - uint newBorrowShares = (int(priorBorrowShares) + shares).safeToUint(); - uint newCollateral = (int(collateral[borrower][bucket]) + collat).safeToUint(); + // Keep this next computation outside of the if-then-else. + uint newTotalSupply = (int(totalSupply[bucket]) - int(priorBorrow) - borrow).safeToUint(); - totalBorrow[bucket] = (int(totalBorrow[bucket]) + borrow).safeToUint(); + uint newCollateral = uint(int(collateral[borrower][bucket]) + collat); if (newCollateral == 0) { - // Realize the bad debt. + totalBorrow[bucket] -= priorBorrow; totalBorrowShares[bucket] -= priorBorrowShares; borrowShare[borrower][bucket] = 0; + // Realize the bad debt. + totalSupply[bucket] = newTotalSupply; } else { + totalBorrow[bucket] = (int(totalBorrow[bucket]) + borrow).safeToUint(); totalBorrowShares[bucket] = (int(totalBorrowShares[bucket]) + shares).safeToUint(); - borrowShare[borrower][bucket] = newBorrowShares; + borrowShare[borrower][bucket] = (int(priorBorrowShares) + shares).safeToUint(); } collateral[borrower][bucket] = newCollateral; } From a65f7439eb07392293bd3320046f688723c9930e Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Thu, 29 Jun 2023 12:15:30 +0200 Subject: [PATCH 027/112] test: add realize bad debt test --- test/forge/Market.t.sol | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index e7df1e7b0..4c2a7feae 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -55,6 +55,7 @@ contract MarketTest is Test { function supplyBalance(uint bucket, address user) internal view returns (uint) { uint supplyShares = market.supplyShare(user, bucket); + if (supplyShares == 0) return 0; uint totalShares = market.totalSupplyShares(bucket); uint totalSupply = market.totalSupply(bucket); return supplyShares.wMul(totalSupply).wDiv(totalShares); @@ -62,6 +63,7 @@ contract MarketTest is Test { function borrowBalance(uint bucket, address user) internal view returns (uint) { uint borrowerShares = market.borrowShare(user, bucket); + if (borrowerShares == 0) return 0; uint totalShares = market.totalBorrowShares(bucket); uint totalBorrow = market.totalBorrow(bucket); return borrowerShares.wMul(totalBorrow).wDiv(totalShares); @@ -300,6 +302,59 @@ contract MarketTest is Test { ); } + function testRealizeBadDebt(uint bucket, uint amountLent) public { + borrowableOracle.setPrice(1e18); + amountLent = bound(amountLent, 1000, 2 ** 64); + vm.assume(bucket < N); + + uint amountCollateral = amountLent; + uint lLTV = bucketToLLTV(bucket); + uint borrowingPower = amountCollateral.wMul(lLTV); + uint amountBorrowed = borrowingPower.wMul(0.8e18); + uint maxCollat = type(uint).max; + + borrowableAsset.setBalance(address(this), amountLent); + collateralAsset.setBalance(borrower, amountCollateral); + borrowableAsset.setBalance(liquidator, amountBorrowed); + + // Lend + borrowableAsset.approve(address(market), type(uint).max); + market.modifyDeposit(int(amountLent), bucket); + + // Borrow + vm.startPrank(borrower); + collateralAsset.approve(address(market), type(uint).max); + market.modifyCollateral(int(amountCollateral), bucket); + market.modifyBorrow(int(amountBorrowed), bucket); + vm.stopPrank(); + + // Price change + borrowableOracle.setPrice(100e18); + + uint liquidatorNetworthBefore = networth(liquidator); + + // Liquidate + Market.Liquidation[] memory liquidationData = new Market.Liquidation[](1); + liquidationData[0] = Market.Liquidation(bucket, borrower, maxCollat); + vm.startPrank(liquidator); + borrowableAsset.approve(address(market), type(uint).max); + (int sumCollat, int sumBorrow) = market.batchLiquidate(liquidationData); + vm.stopPrank(); + + uint liquidatorNetworthAfter = networth(liquidator); + + assertGt(liquidatorNetworthAfter, liquidatorNetworthBefore, "liquidator's networth"); + assertEq(sumCollat, -int(amountCollateral), "collateral seized"); + assertLt(sumBorrow, 0, "borrow repaid"); + assertEq(int(borrowBalance(bucket, borrower)), 0, "collateral balance borrower"); + assertEq(int(market.collateral(borrower, bucket)), 0, "collateral balance borrower"); + int expectedBadDebt = int(amountBorrowed) + sumBorrow; + assertGt(expectedBadDebt, 0, "positive bad debt"); + assertApproxEqAbs( + int(supplyBalance(bucket, address(this))), int(amountLent) - expectedBadDebt, 10, "realized bad debt" + ); + } + function testTwoUsersSupply(uint firstAmount, uint secondAmount, uint bucket) public { vm.assume(bucket < N); firstAmount = bound(firstAmount, 1, 2 ** 64); From f7e84a3f428c5db80016984db049470fc9282742 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Sun, 2 Jul 2023 23:47:32 +0200 Subject: [PATCH 028/112] test: fix id computation --- test/hardhat/Market.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index e82b1ba79..d19748d41 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -1,9 +1,7 @@ import { hexZeroPad } from "@ethersproject/bytes"; -import { keccak256 } from "@ethersproject/keccak256"; -import { toUtf8Bytes } from "@ethersproject/strings"; import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { BigNumber, Wallet, constants } from "ethers"; +import { BigNumber, Wallet, constants, utils } from "ethers"; import hre from "hardhat"; import { Blue, OracleMock, ERC20Mock } from "types"; @@ -72,9 +70,11 @@ describe("Blue", () => { lLTV: BigNumber.WAD, }; - const encodedInfo = toUtf8Bytes(JSON.stringify(info)); - const hashedInfoHex = keccak256(encodedInfo); - id = Buffer.from(hashedInfoHex.slice(2), "hex"); + const abiCoder = new utils.AbiCoder(); + const values = Object.values(info); + const encodedInfo = abiCoder.encode(['address', 'address', 'address', 'address', 'uint256'], values); + + id = Buffer.from(utils.keccak256(encodedInfo).slice(2), "hex"); await blue.connect(signers[0]).createMarket(info); }); From 4e7b5868938aa35d5207cf65b57497e2b5b9093e Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 3 Jul 2023 00:02:32 +0200 Subject: [PATCH 029/112] feat: split entry-points --- src/Blue.sol | 104 ++++++++++++++++++++---------- src/libraries/MathLib.sol | 16 ----- src/libraries/SafeTransferLib.sol | 9 +-- test/forge/Blue.t.sol | 93 +++++++++++++------------- test/hardhat/Market.spec.ts | 16 +++-- 5 files changed, 130 insertions(+), 108 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index a9b17b3fa..8cc2ddb7b 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -26,7 +26,6 @@ function irm(uint utilization) pure returns (uint) { } contract Blue { - using MathLib for int; using MathLib for uint; using SafeTransferLib for IERC20; @@ -58,13 +57,12 @@ contract Blue { accrueInterests(id); } - // Suppliers position management. + // Supply management. - /// @dev positive amount to deposit. - function modifyDeposit(Info calldata info, int amount) external { + function supply(Info calldata info, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(info))); - if (amount == 0) return; require(lastUpdate[id] != 0, "unknown market"); + require(amount > 0, "zero amount"); accrueInterests(id); @@ -72,62 +70,102 @@ contract Blue { supplyShare[id][msg.sender] = 1e18; totalSupplyShares[id] = 1e18; } else { - int shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); - supplyShare[id][msg.sender] = (int(supplyShare[id][msg.sender]) + shares).safeToUint(); - totalSupplyShares[id] = (int(totalSupplyShares[id]) + shares).safeToUint(); + uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); + supplyShare[id][msg.sender] += supplyShare[id][msg.sender] + shares; + totalSupplyShares[id] += shares; } - // No need to check if the integer is positive. - totalSupply[id] = uint(int(totalSupply[id]) + amount); + totalSupply[id] += amount; - if (amount < 0) require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); + info.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); + } + + function withdraw(Info calldata info, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + require(lastUpdate[id] != 0, "unknown market"); + require(amount > 0, "zero amount"); + + accrueInterests(id); + + uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); + supplyShare[id][msg.sender] -= shares; + totalSupplyShares[id] -= shares; + + totalSupply[id] -= amount; - info.borrowableAsset.handleTransfer({user: msg.sender, amountIn: amount}); + require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); + + info.borrowableAsset.safeTransfer(msg.sender, amount); } - // Borrowers position management. + // Borrow management. - /// @dev positive amount to borrow (to discuss). - function modifyBorrow(Info calldata info, int amount) external { + function borrow(Info calldata info, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(info))); - if (amount == 0) return; require(lastUpdate[id] != 0, "unknown market"); + require(amount > 0, "zero amount"); accrueInterests(id); - if (totalBorrow[id] == 0 && amount > 0) { + if (totalBorrow[id] == 0) { borrowShare[id][msg.sender] = 1e18; totalBorrowShares[id] = 1e18; } else { - int shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); - borrowShare[id][msg.sender] = (int(borrowShare[id][msg.sender]) + shares).safeToUint(); - totalBorrowShares[id] = (int(totalBorrowShares[id]) + shares).safeToUint(); + uint shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + borrowShare[id][msg.sender] += shares; + totalBorrowShares[id] += shares; } - // No need to check if the integer is positive. - totalBorrow[id] = uint(int(totalBorrow[id]) + amount); + totalBorrow[id] += amount; - if (amount > 0) { - checkHealth(info, id, msg.sender); - require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); - } + checkHealth(info, id, msg.sender); + require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); + + info.borrowableAsset.safeTransfer(msg.sender, amount); + } + + function repay(Info calldata info, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + require(lastUpdate[id] != 0, "unknown market"); + require(amount > 0, "zero amount"); + + accrueInterests(id); + + uint shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + borrowShare[id][msg.sender] -= shares; + totalBorrowShares[id] -= shares; + + totalBorrow[id] -= amount; + + info.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); + } + + // Collateral management. + + function supplyCollateral(Info calldata info, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(info))); + require(lastUpdate[id] != 0, "unknown market"); + require(amount > 0, "zero amount"); + + accrueInterests(id); + + collateral[id][msg.sender] += amount; - info.borrowableAsset.handleTransfer({user: msg.sender, amountIn: -amount}); + info.collateralAsset.transferFrom(msg.sender, address(this), amount); } - /// @dev positive amount to deposit. - function modifyCollateral(Info calldata info, int amount) external { + function withdrawCollateral(Info calldata info, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(info))); - if (amount == 0) return; require(lastUpdate[id] != 0, "unknown market"); + require(amount > 0, "zero amount"); accrueInterests(id); - collateral[id][msg.sender] = (int(collateral[id][msg.sender]) + amount).safeToUint(); + collateral[id][msg.sender] -= amount; - if (amount < 0) checkHealth(info, id, msg.sender); + checkHealth(info, id, msg.sender); - info.collateralAsset.handleTransfer({user: msg.sender, amountIn: amount}); + info.collateralAsset.transfer(msg.sender, amount); } // Interests management. diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index a965c8e5f..9dfd7145f 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -10,24 +10,8 @@ library MathLib { z = (x * y) / WAD; } - /// @dev Rounds towards zero. - function wMul(int x, uint y) internal pure returns (int z) { - z = (x * int(y)) / int(WAD); - } - /// @dev Rounds towards zero. function wDiv(uint x, uint y) internal pure returns (uint z) { z = (x * WAD) / y; } - - /// @dev Rounds towards zero. - function wDiv(int x, uint y) internal pure returns (int z) { - z = (x * int(WAD)) / int(y); - } - - /// @dev Reverts if x is negative. - function safeToUint(int x) internal pure returns (uint z) { - require(x >= 0, "negative"); - z = uint(x); - } } diff --git a/src/libraries/SafeTransferLib.sol b/src/libraries/SafeTransferLib.sol index 1198bc41f..1a2cb4255 100644 --- a/src/libraries/SafeTransferLib.sol +++ b/src/libraries/SafeTransferLib.sol @@ -8,12 +8,7 @@ import {IERC20} from "src/interfaces/IERC20.sol"; /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { - function handleTransfer(IERC20 token, address user, int amountIn) internal { - if (amountIn > 0) return safeTransferFrom(token, user, address(this), uint(amountIn)); - return safeTransfer(token, user, uint(-amountIn)); - } - - function safeTransferFrom(IERC20 token, address from, address to, uint amount) private { + function safeTransferFrom(IERC20 token, address from, address to, uint amount) internal { bool success; /// @solidity memory-safe-assembly @@ -43,7 +38,7 @@ library SafeTransferLib { require(success, "TRANSFER_FROM_FAILED"); } - function safeTransfer(IERC20 token, address to, uint amount) private { + function safeTransfer(IERC20 token, address to, uint amount) internal { bool success; /// @solidity memory-safe-assembly diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 368a454db..1c0e0c94c 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -62,7 +62,7 @@ contract BlueTest is Test { amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); - blue.modifyDeposit(info, int(amount)); + blue.supply(info, amount); assertEq(blue.supplyShare(id, address(this)), 1e18); assertEq(borrowableAsset.balanceOf(address(this)), 0); @@ -70,26 +70,26 @@ contract BlueTest is Test { } function testBorrow(uint amountLent, uint amountBorrowed) public { - amountLent = bound(amountLent, 0, 2 ** 64); - amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); + amountLent = bound(amountLent, 1, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amountLent); - blue.modifyDeposit(info, int(amountLent)); + blue.supply(info, amountLent); if (amountBorrowed == 0) { - blue.modifyBorrow(info, int(amountBorrowed)); + blue.borrow(info, amountBorrowed); return; } if (amountBorrowed > amountLent) { vm.prank(borrower); vm.expectRevert("not enough liquidity"); - blue.modifyBorrow(info, int(amountBorrowed)); + blue.borrow(info, amountBorrowed); return; } vm.prank(borrower); - blue.modifyBorrow(info, int(amountBorrowed)); + blue.borrow(info, amountBorrowed); assertEq(blue.borrowShare(id, borrower), 1e18); assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed); @@ -98,14 +98,15 @@ contract BlueTest is Test { function testWithdraw(uint amountLent, uint amountWithdrawn, uint amountBorrowed) public { amountLent = bound(amountLent, 1, 2 ** 64); + amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); vm.assume(amountLent >= amountBorrowed); - vm.assume(int(amountWithdrawn) >= 0); borrowableAsset.setBalance(address(this), amountLent); - blue.modifyDeposit(info, int(amountLent)); + blue.supply(info, amountLent); vm.prank(borrower); - blue.modifyBorrow(info, int(amountBorrowed)); + blue.borrow(info, amountBorrowed); if (amountWithdrawn > amountLent - amountBorrowed) { if (amountWithdrawn > amountLent) { @@ -113,11 +114,11 @@ contract BlueTest is Test { } else { vm.expectRevert("not enough liquidity"); } - blue.modifyDeposit(info, -int(amountWithdrawn)); + blue.withdraw(info, amountWithdrawn); return; } - blue.modifyDeposit(info, -int(amountWithdrawn)); + blue.withdraw(info, amountWithdrawn); assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3); assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn); @@ -130,9 +131,9 @@ contract BlueTest is Test { uint priceCollateral, uint priceBorrowable ) public { - amountBorrowed = bound(amountBorrowed, 0, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); priceBorrowable = bound(priceBorrowable, 0, 2 ** 64); - amountCollateral = bound(amountCollateral, 0, 2 ** 64); + amountCollateral = bound(amountCollateral, 1, 2 ** 64); priceCollateral = bound(priceCollateral, 0, 2 ** 64); borrowableOracle.setPrice(priceBorrowable); @@ -141,34 +142,34 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), amountBorrowed); collateralAsset.setBalance(borrower, amountCollateral); - blue.modifyDeposit(info, int(amountBorrowed)); + blue.supply(info, amountBorrowed); vm.prank(borrower); - blue.modifyCollateral(info, int(amountCollateral)); + blue.supplyCollateral(info, amountCollateral); uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(lLTV))) { vm.prank(borrower); - blue.modifyBorrow(info, int(amountBorrowed)); + blue.borrow(info, amountBorrowed); } else { vm.prank(borrower); vm.expectRevert("not enough collateral"); - blue.modifyBorrow(info, int(amountBorrowed)); + blue.borrow(info, amountBorrowed); } } function testRepay(uint amountLent, uint amountBorrowed, uint amountRepaid) public { amountLent = bound(amountLent, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, amountLent); - amountRepaid = bound(amountRepaid, 0, amountBorrowed); + amountRepaid = bound(amountRepaid, 1, amountBorrowed); borrowableAsset.setBalance(address(this), amountLent); - blue.modifyDeposit(info, int(amountLent)); + blue.supply(info, amountLent); vm.startPrank(borrower); - blue.modifyBorrow(info, int(amountBorrowed)); - blue.modifyBorrow(info, -int(amountRepaid)); + blue.borrow(info, amountBorrowed); + blue.repay(info, amountRepaid); vm.stopPrank(); assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3); @@ -177,10 +178,10 @@ contract BlueTest is Test { } function testDepositCollateral(uint amount) public { - vm.assume(int(amount) >= 0); + amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); - blue.modifyCollateral(info, int(amount)); + blue.supplyCollateral(info, amount); assertEq(blue.collateral(id, address(this)), amount); assertEq(collateralAsset.balanceOf(address(this)), 0); @@ -188,19 +189,19 @@ contract BlueTest is Test { } function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn) public { - vm.assume(int(amountDeposited) > 0); - vm.assume(int(amountWithdrawn) > 0); + amountDeposited = bound(amountDeposited, 1, 2 ** 64); + amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); collateralAsset.setBalance(address(this), amountDeposited); - blue.modifyCollateral(info, int(amountDeposited)); + blue.supplyCollateral(info, amountDeposited); if (amountWithdrawn > amountDeposited) { - vm.expectRevert("negative"); - blue.modifyCollateral(info, -int(amountWithdrawn)); + vm.expectRevert(stdError.arithmeticError); + blue.withdrawCollateral(info, amountWithdrawn); return; } - blue.modifyCollateral(info, -int(amountWithdrawn)); + blue.withdrawCollateral(info, amountWithdrawn); assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn); assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn); @@ -209,14 +210,14 @@ contract BlueTest is Test { function testTwoUsersSupply(uint firstAmount, uint secondAmount) public { firstAmount = bound(firstAmount, 1, 2 ** 64); - secondAmount = bound(secondAmount, 0, 2 ** 64); + secondAmount = bound(secondAmount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), firstAmount); - blue.modifyDeposit(info, int(firstAmount)); + blue.supply(info, firstAmount); borrowableAsset.setBalance(borrower, secondAmount); vm.prank(borrower); - blue.modifyDeposit(info, int(secondAmount)); + blue.supply(info, secondAmount); assertEq(blue.supplyShare(id, address(this)), 1e18); assertEq(blue.supplyShare(id, borrower), secondAmount * 1e18 / firstAmount); @@ -225,37 +226,37 @@ contract BlueTest is Test { function testModifyDepositUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); - blue.modifyDeposit(infoFuzz, 1); + blue.supply(infoFuzz, 1); } function testModifyBorrowUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); - blue.modifyBorrow(infoFuzz, 1); + blue.borrow(infoFuzz, 1); } function testModifyCollateralUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); - blue.modifyCollateral(infoFuzz, 1); + blue.supplyCollateral(infoFuzz, 1); } - function testWithdrawEmptyMarket(int amount) public { - vm.assume(amount < 0); + function testWithdrawEmptyMarket(uint amount) public { + vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); - blue.modifyDeposit(info, amount); + blue.withdraw(info, amount); } - function testRepayEmptyMarket(int amount) public { - vm.assume(amount < 0); + function testRepayEmptyMarket(uint amount) public { + vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); - blue.modifyBorrow(info, amount); + blue.repay(info, amount); } - function testWithdrawCollateralEmptyMarket(int amount) public { - vm.assume(amount < 0); - vm.expectRevert("negative"); - blue.modifyCollateral(info, amount); + function testWithdrawCollateralEmptyMarket(uint amount) public { + vm.assume(amount > 0); + vm.expectRevert(stdError.arithmeticError); + blue.withdrawCollateral(info, amount); } } diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index d19748d41..2b21d1cbc 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -101,18 +101,22 @@ describe("Blue", () => { let supplyOnly: boolean = random() < 2 / 3; if (supplyOnly) { - await blue.connect(user).modifyDeposit(info, amount); - await blue.connect(user).modifyDeposit(info, amount.div(2).mul(-1)); + if (amount > BigNumber.from(0)) { + await blue.connect(user).supply(info, amount); + await blue.connect(user).withdraw(info, amount.div(2)); + } } else { const totalSupply = await blue.totalSupply(id); const totalBorrow = await blue.totalBorrow(id); let liq = BigNumber.from(totalSupply).sub(BigNumber.from(totalBorrow)); amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); - await blue.connect(user).modifyCollateral(info, amount); - await blue.connect(user).modifyBorrow(info, amount.div(2)); - await blue.connect(user).modifyBorrow(info, amount.div(4).mul(-1)); - await blue.connect(user).modifyCollateral(info, amount.div(8).mul(-1)); + if (amount > BigNumber.from(0)) { + await blue.connect(user).supplyCollateral(info, amount); + await blue.connect(user).borrow(info, amount.div(2)); + await blue.connect(user).repay(info, amount.div(4)); + await blue.connect(user).withdrawCollateral(info, amount.div(8)); + } } } }); From e901da7310919cd420402e7d864a9c657bbbecdb Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 3 Jul 2023 10:43:00 +0200 Subject: [PATCH 030/112] chore: revert file renaming --- src/{Blue.sol => Market.sol} | 0 test/forge/{Blue.t.sol => Market.t.sol} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{Blue.sol => Market.sol} (100%) rename test/forge/{Blue.t.sol => Market.t.sol} (99%) diff --git a/src/Blue.sol b/src/Market.sol similarity index 100% rename from src/Blue.sol rename to src/Market.sol diff --git a/test/forge/Blue.t.sol b/test/forge/Market.t.sol similarity index 99% rename from test/forge/Blue.t.sol rename to test/forge/Market.t.sol index 1c0e0c94c..e652b4aff 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Market.t.sol @@ -7,7 +7,7 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -import "src/Blue.sol"; +import "src/Market.sol"; import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; From 306a6743679a3003d854077785971edac71e1093 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 3 Jul 2023 10:56:37 +0200 Subject: [PATCH 031/112] style: variable renaming --- src/Market.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 8cc2ddb7b..7c4a512f8 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -171,15 +171,15 @@ contract Blue { // Interests management. function accrueInterests(Id id) internal { - uint bucketTotalSupply = totalSupply[id]; + uint marketTotalSupply = totalSupply[id]; - if (bucketTotalSupply != 0) { - uint bucketTotalBorrow = totalBorrow[id]; - uint utilization = bucketTotalBorrow.wDiv(bucketTotalSupply); + if (marketTotalSupply != 0) { + uint marketTotalBorrow = totalBorrow[id]; + uint utilization = marketTotalBorrow.wDiv(marketTotalSupply); uint borrowRate = irm(utilization); - uint accruedInterests = bucketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); - totalSupply[id] = bucketTotalSupply + accruedInterests; - totalBorrow[id] = bucketTotalBorrow + accruedInterests; + uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); + totalSupply[id] = marketTotalSupply + accruedInterests; + totalBorrow[id] = marketTotalBorrow + accruedInterests; } lastUpdate[id] = block.timestamp; @@ -189,7 +189,7 @@ contract Blue { function checkHealth(Info calldata info, Id id, address user) public view { if (borrowShare[id][user] > 0) { - // totalBorrowShares[bucket] > 0 because borrowShare[user][bucket] > 0. + // totalBorrowShares[id] > 0 because borrowShare[id[user] > 0. uint borrowValue = borrowShare[id][user].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul( IOracle(info.borrowableOracle).price() ); From d81e1ca02475ef562a83cf8dab6a9a8e73a3d918 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 3 Jul 2023 23:54:40 +0200 Subject: [PATCH 032/112] fix: user supply share accounting --- src/Market.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 7c4a512f8..2f663d670 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -66,12 +66,12 @@ contract Blue { accrueInterests(id); - if (totalSupply[id] == 0 && amount > 0) { + if (totalSupply[id] == 0) { supplyShare[id][msg.sender] = 1e18; totalSupplyShares[id] = 1e18; } else { uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); - supplyShare[id][msg.sender] += supplyShare[id][msg.sender] + shares; + supplyShare[id][msg.sender] += shares; totalSupplyShares[id] += shares; } From 62e10015d91fd522fb0d8cc44d22e1ae41768ca0 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 10:47:19 +0200 Subject: [PATCH 033/112] test: unknown market --- test/forge/Market.t.sol | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index e652b4aff..387bef4a0 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -223,23 +223,40 @@ contract BlueTest is Test { assertEq(blue.supplyShare(id, borrower), secondAmount * 1e18 / firstAmount); } - function testModifyDepositUnknownMarket(Info memory infoFuzz) public { + function testSupplyUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); blue.supply(infoFuzz, 1); } - function testModifyBorrowUnknownMarket(Info memory infoFuzz) public { + function testWithdrawUnknownMarket(Info memory infoFuzz) public { + vm.assume(neq(infoFuzz, info)); + vm.expectRevert("unknown market"); + blue.withdraw(infoFuzz, 1); + } + + function testBorrowUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); blue.borrow(infoFuzz, 1); } - function testModifyCollateralUnknownMarket(Info memory infoFuzz) public { + function testRepayUnknownMarket(Info memory infoFuzz) public { + vm.assume(neq(infoFuzz, info)); + vm.expectRevert("unknown market"); + blue.repay(infoFuzz, 1); + } + + function testSupplyCollateralUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); blue.supplyCollateral(infoFuzz, 1); } + function testWithdrawCollateralUnknownMarket(Info memory infoFuzz) public { + vm.assume(neq(infoFuzz, info)); + vm.expectRevert("unknown market"); + blue.withdrawCollateral(infoFuzz, 1); + } function testWithdrawEmptyMarket(uint amount) public { vm.assume(amount > 0); From 478fe5bbfe33f32a541de43ed5d3f9ef67029c1b Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 10:57:36 +0200 Subject: [PATCH 034/112] fix: checkHealth private --- src/Market.sol | 8 ++++---- test/forge/Market.t.sol | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 2f663d670..8de33da25 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -90,7 +90,7 @@ contract Blue { uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); supplyShare[id][msg.sender] -= shares; totalSupplyShares[id] -= shares; - + totalSupply[id] -= amount; require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); @@ -170,7 +170,7 @@ contract Blue { // Interests management. - function accrueInterests(Id id) internal { + function accrueInterests(Id id) private { uint marketTotalSupply = totalSupply[id]; if (marketTotalSupply != 0) { @@ -187,9 +187,9 @@ contract Blue { // Health check. - function checkHealth(Info calldata info, Id id, address user) public view { + function checkHealth(Info calldata info, Id id, address user) private view { if (borrowShare[id][user] > 0) { - // totalBorrowShares[id] > 0 because borrowShare[id[user] > 0. + // totalBorrowShares[id] > 0 because borrowShare[id][user] > 0. uint borrowValue = borrowShare[id][user].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul( IOracle(info.borrowableOracle).price() ); diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 387bef4a0..9ce40e01c 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -252,6 +252,7 @@ contract BlueTest is Test { vm.expectRevert("unknown market"); blue.supplyCollateral(infoFuzz, 1); } + function testWithdrawCollateralUnknownMarket(Info memory infoFuzz) public { vm.assume(neq(infoFuzz, info)); vm.expectRevert("unknown market"); From 3eb982792484a9e5459c222aa68d792dee71e536 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 11:09:07 +0200 Subject: [PATCH 035/112] style: info renaming --- src/Market.sol | 56 +++++++++++----------- test/forge/Market.t.sol | 96 ++++++++++++++++++------------------- test/hardhat/Market.spec.ts | 29 ++++++----- 3 files changed, 92 insertions(+), 89 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 8de33da25..c799e38f2 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -10,8 +10,8 @@ import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; // Market id. type Id is bytes32; -// Market info. -struct Info { +// Market. +struct Market { IERC20 borrowableAsset; IERC20 collateralAsset; IOracle borrowableOracle; @@ -50,8 +50,8 @@ contract Blue { // Markets management. - function createMarket(Info calldata info) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function createMarket(Market calldata market) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id); @@ -59,8 +59,8 @@ contract Blue { // Supply management. - function supply(Info calldata info, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function supply(Market calldata market, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -77,11 +77,11 @@ contract Blue { totalSupply[id] += amount; - info.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); + market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); } - function withdraw(Info calldata info, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function withdraw(Market calldata market, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -95,13 +95,13 @@ contract Blue { require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); - info.borrowableAsset.safeTransfer(msg.sender, amount); + market.borrowableAsset.safeTransfer(msg.sender, amount); } // Borrow management. - function borrow(Info calldata info, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function borrow(Market calldata market, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -118,14 +118,14 @@ contract Blue { totalBorrow[id] += amount; - checkHealth(info, id, msg.sender); + checkHealth(market, id, msg.sender); require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); - info.borrowableAsset.safeTransfer(msg.sender, amount); + market.borrowableAsset.safeTransfer(msg.sender, amount); } - function repay(Info calldata info, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function repay(Market calldata market, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -137,13 +137,13 @@ contract Blue { totalBorrow[id] -= amount; - info.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); + market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); } // Collateral management. - function supplyCollateral(Info calldata info, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function supplyCollateral(Market calldata market, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -151,11 +151,11 @@ contract Blue { collateral[id][msg.sender] += amount; - info.collateralAsset.transferFrom(msg.sender, address(this), amount); + market.collateralAsset.transferFrom(msg.sender, address(this), amount); } - function withdrawCollateral(Info calldata info, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(info))); + function withdrawCollateral(Market calldata market, uint amount) external { + Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -163,9 +163,9 @@ contract Blue { collateral[id][msg.sender] -= amount; - checkHealth(info, id, msg.sender); + checkHealth(market, id, msg.sender); - info.collateralAsset.transfer(msg.sender, amount); + market.collateralAsset.transfer(msg.sender, amount); } // Interests management. @@ -187,14 +187,14 @@ contract Blue { // Health check. - function checkHealth(Info calldata info, Id id, address user) private view { + function checkHealth(Market calldata market, Id id, address user) private view { if (borrowShare[id][user] > 0) { // totalBorrowShares[id] > 0 because borrowShare[id][user] > 0. uint borrowValue = borrowShare[id][user].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul( - IOracle(info.borrowableOracle).price() + IOracle(market.borrowableOracle).price() ); - uint collateralValue = collateral[id][user].wMul(IOracle(info.collateralOracle).price()); - require(collateralValue.wMul(info.lLTV) >= borrowValue, "not enough collateral"); + uint collateralValue = collateral[id][user].wMul(IOracle(market.collateralOracle).price()); + require(collateralValue.wMul(market.lLTV) >= borrowValue, "not enough collateral"); } } } diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 9ce40e01c..d9e6f8f26 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -22,7 +22,7 @@ contract BlueTest is Test { ERC20 private collateralAsset; Oracle private borrowableOracle; Oracle private collateralOracle; - Info public info; + Market public market; Id public id; function setUp() public { @@ -34,12 +34,12 @@ contract BlueTest is Test { collateralAsset = new ERC20("collateral", "C", 18); borrowableOracle = new Oracle(); collateralOracle = new Oracle(); - info = Info( + market = Market( IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, lLTV ); - id = Id.wrap(keccak256(abi.encode(info))); + id = Id.wrap(keccak256(abi.encode(market))); - blue.createMarket(info); + blue.createMarket(market); // We set the price of the borrowable asset to zero so that borrowers // don't need to deposit any collateral. @@ -62,7 +62,7 @@ contract BlueTest is Test { amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); - blue.supply(info, amount); + blue.supply(market, amount); assertEq(blue.supplyShare(id, address(this)), 1e18); assertEq(borrowableAsset.balanceOf(address(this)), 0); @@ -74,22 +74,22 @@ contract BlueTest is Test { amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(info, amountLent); + blue.supply(market, amountLent); if (amountBorrowed == 0) { - blue.borrow(info, amountBorrowed); + blue.borrow(market, amountBorrowed); return; } if (amountBorrowed > amountLent) { vm.prank(borrower); vm.expectRevert("not enough liquidity"); - blue.borrow(info, amountBorrowed); + blue.borrow(market, amountBorrowed); return; } vm.prank(borrower); - blue.borrow(info, amountBorrowed); + blue.borrow(market, amountBorrowed); assertEq(blue.borrowShare(id, borrower), 1e18); assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed); @@ -103,10 +103,10 @@ contract BlueTest is Test { vm.assume(amountLent >= amountBorrowed); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(info, amountLent); + blue.supply(market, amountLent); vm.prank(borrower); - blue.borrow(info, amountBorrowed); + blue.borrow(market, amountBorrowed); if (amountWithdrawn > amountLent - amountBorrowed) { if (amountWithdrawn > amountLent) { @@ -114,11 +114,11 @@ contract BlueTest is Test { } else { vm.expectRevert("not enough liquidity"); } - blue.withdraw(info, amountWithdrawn); + blue.withdraw(market, amountWithdrawn); return; } - blue.withdraw(info, amountWithdrawn); + blue.withdraw(market, amountWithdrawn); assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3); assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn); @@ -142,20 +142,20 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), amountBorrowed); collateralAsset.setBalance(borrower, amountCollateral); - blue.supply(info, amountBorrowed); + blue.supply(market, amountBorrowed); vm.prank(borrower); - blue.supplyCollateral(info, amountCollateral); + blue.supplyCollateral(market, amountCollateral); uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(lLTV))) { vm.prank(borrower); - blue.borrow(info, amountBorrowed); + blue.borrow(market, amountBorrowed); } else { vm.prank(borrower); vm.expectRevert("not enough collateral"); - blue.borrow(info, amountBorrowed); + blue.borrow(market, amountBorrowed); } } @@ -165,11 +165,11 @@ contract BlueTest is Test { amountRepaid = bound(amountRepaid, 1, amountBorrowed); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(info, amountLent); + blue.supply(market, amountLent); vm.startPrank(borrower); - blue.borrow(info, amountBorrowed); - blue.repay(info, amountRepaid); + blue.borrow(market, amountBorrowed); + blue.repay(market, amountRepaid); vm.stopPrank(); assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3); @@ -181,7 +181,7 @@ contract BlueTest is Test { amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); - blue.supplyCollateral(info, amount); + blue.supplyCollateral(market, amount); assertEq(blue.collateral(id, address(this)), amount); assertEq(collateralAsset.balanceOf(address(this)), 0); @@ -193,15 +193,15 @@ contract BlueTest is Test { amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); collateralAsset.setBalance(address(this), amountDeposited); - blue.supplyCollateral(info, amountDeposited); + blue.supplyCollateral(market, amountDeposited); if (amountWithdrawn > amountDeposited) { vm.expectRevert(stdError.arithmeticError); - blue.withdrawCollateral(info, amountWithdrawn); + blue.withdrawCollateral(market, amountWithdrawn); return; } - blue.withdrawCollateral(info, amountWithdrawn); + blue.withdrawCollateral(market, amountWithdrawn); assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn); assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn); @@ -213,72 +213,72 @@ contract BlueTest is Test { secondAmount = bound(secondAmount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), firstAmount); - blue.supply(info, firstAmount); + blue.supply(market, firstAmount); borrowableAsset.setBalance(borrower, secondAmount); vm.prank(borrower); - blue.supply(info, secondAmount); + blue.supply(market, secondAmount); assertEq(blue.supplyShare(id, address(this)), 1e18); assertEq(blue.supplyShare(id, borrower), secondAmount * 1e18 / firstAmount); } - function testSupplyUnknownMarket(Info memory infoFuzz) public { - vm.assume(neq(infoFuzz, info)); + function testSupplyUnknownMarket(Market memory marketFuzz) public { + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.supply(infoFuzz, 1); + blue.supply(marketFuzz, 1); } - function testWithdrawUnknownMarket(Info memory infoFuzz) public { - vm.assume(neq(infoFuzz, info)); + function testWithdrawUnknownMarket(Market memory marketFuzz) public { + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.withdraw(infoFuzz, 1); + blue.withdraw(marketFuzz, 1); } - function testBorrowUnknownMarket(Info memory infoFuzz) public { - vm.assume(neq(infoFuzz, info)); + function testBorrowUnknownMarket(Market memory marketFuzz) public { + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.borrow(infoFuzz, 1); + blue.borrow(marketFuzz, 1); } - function testRepayUnknownMarket(Info memory infoFuzz) public { - vm.assume(neq(infoFuzz, info)); + function testRepayUnknownMarket(Market memory marketFuzz) public { + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.repay(infoFuzz, 1); + blue.repay(marketFuzz, 1); } - function testSupplyCollateralUnknownMarket(Info memory infoFuzz) public { - vm.assume(neq(infoFuzz, info)); + function testSupplyCollateralUnknownMarket(Market memory marketFuzz) public { + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.supplyCollateral(infoFuzz, 1); + blue.supplyCollateral(marketFuzz, 1); } - function testWithdrawCollateralUnknownMarket(Info memory infoFuzz) public { - vm.assume(neq(infoFuzz, info)); + function testWithdrawCollateralUnknownMarket(Market memory marketFuzz) public { + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.withdrawCollateral(infoFuzz, 1); + blue.withdrawCollateral(marketFuzz, 1); } function testWithdrawEmptyMarket(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); - blue.withdraw(info, amount); + blue.withdraw(market, amount); } function testRepayEmptyMarket(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); - blue.repay(info, amount); + blue.repay(market, amount); } function testWithdrawCollateralEmptyMarket(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.arithmeticError); - blue.withdrawCollateral(info, amount); + blue.withdrawCollateral(market, amount); } } -function neq(Info memory a, Info memory b) pure returns (bool) { +function neq(Market memory a, Market memory b) pure returns (bool) { return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV; } diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 2b21d1cbc..1987438d4 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -18,7 +18,7 @@ function random() { return (next() - 1) / 2147483646; } -interface Info { +interface Market { borrowableAsset: string; collateralAsset: string; borrowableOracle: string; @@ -34,7 +34,7 @@ describe("Blue", () => { let collateral: ERC20Mock; let borrowableOracle: OracleMock; let collateralOracle: OracleMock; - let info: Info; + let market: Market; let id: Buffer; const initBalance = constants.MaxUint256.div(2); @@ -62,7 +62,7 @@ describe("Blue", () => { blue = await BlueFactory.deploy(); - info = { + market = { borrowableAsset: borrowable.address, collateralAsset: collateral.address, borrowableOracle: borrowableOracle.address, @@ -71,12 +71,15 @@ describe("Blue", () => { }; const abiCoder = new utils.AbiCoder(); - const values = Object.values(info); - const encodedInfo = abiCoder.encode(['address', 'address', 'address', 'address', 'uint256'], values); + const values = Object.values(market); + const encodedMarket = abiCoder.encode( + ["address", "address", "address", "address", "uint256"], + values + ); - id = Buffer.from(utils.keccak256(encodedInfo).slice(2), "hex"); + id = Buffer.from(utils.keccak256(encodedMarket).slice(2), "hex"); - await blue.connect(signers[0]).createMarket(info); + await blue.connect(signers[0]).createMarket(market); }); it("should simulate gas cost", async () => { @@ -102,8 +105,8 @@ describe("Blue", () => { let supplyOnly: boolean = random() < 2 / 3; if (supplyOnly) { if (amount > BigNumber.from(0)) { - await blue.connect(user).supply(info, amount); - await blue.connect(user).withdraw(info, amount.div(2)); + await blue.connect(user).supply(market, amount); + await blue.connect(user).withdraw(market, amount.div(2)); } } else { const totalSupply = await blue.totalSupply(id); @@ -112,10 +115,10 @@ describe("Blue", () => { amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); if (amount > BigNumber.from(0)) { - await blue.connect(user).supplyCollateral(info, amount); - await blue.connect(user).borrow(info, amount.div(2)); - await blue.connect(user).repay(info, amount.div(4)); - await blue.connect(user).withdrawCollateral(info, amount.div(8)); + await blue.connect(user).supplyCollateral(market, amount); + await blue.connect(user).borrow(market, amount.div(2)); + await blue.connect(user).repay(market, amount.div(4)); + await blue.connect(user).withdrawCollateral(market, amount.div(8)); } } } From fef155cdba4b70aa3bee17aa159c9fccbd83cea7 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 11:16:07 +0200 Subject: [PATCH 036/112] chore: settle solidity pragma --- src/Market.sol | 2 +- src/interfaces/IERC20.sol | 2 +- src/interfaces/IOracle.sol | 2 +- src/libraries/SafeTransferLib.sol | 2 +- src/mocks/ERC20Mock.sol | 2 +- src/mocks/OracleMock.sol | 2 +- test/forge/Market.t.sol | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index c799e38f2..6f7cb8148 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import {IERC20} from "src/interfaces/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; diff --git a/src/interfaces/IERC20.sol b/src/interfaces/IERC20.sol index 73f30ca56..5a1f7b909 100644 --- a/src/interfaces/IERC20.sol +++ b/src/interfaces/IERC20.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity >=0.5.0; interface IERC20 { function transferFrom(address, address, uint) external; diff --git a/src/interfaces/IOracle.sol b/src/interfaces/IOracle.sol index 90f640c7c..5d795f49c 100644 --- a/src/interfaces/IOracle.sol +++ b/src/interfaces/IOracle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity >=0.5.0; interface IOracle { function price() external view returns (uint); diff --git a/src/libraries/SafeTransferLib.sol b/src/libraries/SafeTransferLib.sol index 1a2cb4255..d752e79c4 100644 --- a/src/libraries/SafeTransferLib.sol +++ b/src/libraries/SafeTransferLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import {IERC20} from "src/interfaces/IERC20.sol"; diff --git a/src/mocks/ERC20Mock.sol b/src/mocks/ERC20Mock.sol index 83ff194aa..7e8bff8b9 100644 --- a/src/mocks/ERC20Mock.sol +++ b/src/mocks/ERC20Mock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import {ERC20} from "solmate/tokens/ERC20.sol"; diff --git a/src/mocks/OracleMock.sol b/src/mocks/OracleMock.sol index 0199c850f..ea45ad6a7 100644 --- a/src/mocks/OracleMock.sol +++ b/src/mocks/OracleMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import {IOracle} from "src/interfaces/IOracle.sol"; diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index d9e6f8f26..ed2a56381 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import {IERC20} from "src/interfaces/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; From bd090ea772526958b87624dfb2668d0fb28f712c Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 11:25:42 +0200 Subject: [PATCH 037/112] test: amount zero --- test/forge/Market.t.sol | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index ed2a56381..b02ccd4ad 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -259,6 +259,26 @@ contract BlueTest is Test { blue.withdrawCollateral(marketFuzz, 1); } + function testAmountZero() public { + vm.expectRevert("amount zero"); + blue.supply(market, 0); + + vm.expectRevert("amount zero"); + blue.withdraw(market, 0); + + vm.expectRevert("amount zero"); + blue.borrow(market, 0); + + vm.expectRevert("amount zero"); + blue.repay(market, 0); + + vm.expectRevert("amount zero"); + blue.supplyCollateral(market, 0); + + vm.expectRevert("amount zero"); + blue.withdrawCollateral(market, 0); + } + function testWithdrawEmptyMarket(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); From d9add8817bad206757d27ec5fd9e2bab54df8acc Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 11:46:06 +0200 Subject: [PATCH 038/112] chore: prettierrc for ts files --- .prettierrc | 3 +++ test/forge/Market.t.sol | 14 +++++++------- test/hardhat/Market.spec.ts | 28 ++++++---------------------- 3 files changed, 16 insertions(+), 29 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..9fcfde7a6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 120 +} diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index b02ccd4ad..4d32ed39c 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -58,7 +58,7 @@ contract BlueTest is Test { assertLe(blue.totalBorrow(id), blue.totalSupply(id)); } - function testDeposit(uint amount) public { + function testSupply(uint amount) public { amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); @@ -177,7 +177,7 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid); } - function testDepositCollateral(uint amount) public { + function testSupplyCollateral(uint amount) public { amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); @@ -262,19 +262,19 @@ contract BlueTest is Test { function testAmountZero() public { vm.expectRevert("amount zero"); blue.supply(market, 0); - + vm.expectRevert("amount zero"); blue.withdraw(market, 0); - + vm.expectRevert("amount zero"); blue.borrow(market, 0); - + vm.expectRevert("amount zero"); blue.repay(market, 0); - + vm.expectRevert("amount zero"); blue.supplyCollateral(market, 0); - + vm.expectRevert("amount zero"); blue.withdrawCollateral(market, 0); } diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 1987438d4..bbbf95af3 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -42,18 +42,12 @@ describe("Blue", () => { beforeEach(async () => { signers = await hre.ethers.getSigners(); - const ERC20MockFactory = await hre.ethers.getContractFactory( - "ERC20Mock", - signers[0] - ); + const ERC20MockFactory = await hre.ethers.getContractFactory("ERC20Mock", signers[0]); borrowable = await ERC20MockFactory.deploy("DAI", "DAI", 18); collateral = await ERC20MockFactory.deploy("Wrapped BTC", "WBTC", 18); - const OracleMockFactory = await hre.ethers.getContractFactory( - "OracleMock", - signers[0] - ); + const OracleMockFactory = await hre.ethers.getContractFactory("OracleMock", signers[0]); borrowableOracle = await OracleMockFactory.deploy(); collateralOracle = await OracleMockFactory.deploy(); @@ -72,10 +66,7 @@ describe("Blue", () => { const abiCoder = new utils.AbiCoder(); const values = Object.values(market); - const encodedMarket = abiCoder.encode( - ["address", "address", "address", "address", "uint256"], - values - ); + const encodedMarket = abiCoder.encode(["address", "address", "address", "address", "uint256"], values); id = Buffer.from(utils.keccak256(encodedMarket).slice(2), "hex"); @@ -86,19 +77,12 @@ describe("Blue", () => { for (let i = 1; i < iterations; ++i) { console.log(i, "/", iterations); - const user = new Wallet( - hexZeroPad(BigNumber.from(i).toHexString(), 32), - hre.ethers.provider - ); + const user = new Wallet(hexZeroPad(BigNumber.from(i).toHexString(), 32), hre.ethers.provider); await setBalance(user.address, initBalance); await borrowable.setBalance(user.address, initBalance); - await borrowable - .connect(user) - .approve(blue.address, constants.MaxUint256); + await borrowable.connect(user).approve(blue.address, constants.MaxUint256); await collateral.setBalance(user.address, initBalance); - await collateral - .connect(user) - .approve(blue.address, constants.MaxUint256); + await collateral.connect(user).approve(blue.address, constants.MaxUint256); let amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); From 2c7d88b49bf5d8032c78457fa0362e4018658cb2 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 11:49:57 +0200 Subject: [PATCH 039/112] test: fix amount zero test --- test/forge/Market.t.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 4d32ed39c..5a9111481 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -260,22 +260,22 @@ contract BlueTest is Test { } function testAmountZero() public { - vm.expectRevert("amount zero"); + vm.expectRevert("zero amount"); blue.supply(market, 0); - vm.expectRevert("amount zero"); + vm.expectRevert("zero amount"); blue.withdraw(market, 0); - vm.expectRevert("amount zero"); + vm.expectRevert("zero amount"); blue.borrow(market, 0); - vm.expectRevert("amount zero"); + vm.expectRevert("zero amount"); blue.repay(market, 0); - vm.expectRevert("amount zero"); + vm.expectRevert("zero amount"); blue.supplyCollateral(market, 0); - vm.expectRevert("amount zero"); + vm.expectRevert("zero amount"); blue.withdrawCollateral(market, 0); } From e57753100c8eb7d855a2e8ba28cd9b74fee665eb Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 12:01:28 +0200 Subject: [PATCH 040/112] chore: update solidity version --- hardhat.config.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index fb98ad52a..7a452f5e9 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,3 +1,8 @@ +import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomicfoundation/hardhat-foundry"; +import "@nomicfoundation/hardhat-network-helpers"; +import "@nomiclabs/hardhat-ethers"; +import "@typechain/hardhat"; import * as dotenv from "dotenv"; import "ethers-maths"; import "hardhat-deal"; @@ -6,12 +11,6 @@ import "hardhat-tracer"; import { HardhatUserConfig } from "hardhat/config"; import "solidity-coverage"; -import "@nomicfoundation/hardhat-chai-matchers"; -import "@nomicfoundation/hardhat-foundry"; -import "@nomicfoundation/hardhat-network-helpers"; -import "@nomiclabs/hardhat-ethers"; -import "@typechain/hardhat"; - dotenv.config(); const config: HardhatUserConfig = { @@ -26,7 +25,7 @@ const config: HardhatUserConfig = { solidity: { compilers: [ { - version: "0.8.19", + version: "0.8.20", settings: { optimizer: { enabled: true, From 49b9450e312a02c845306c4c17a0ade62594738a Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 4 Jul 2023 15:32:29 +0200 Subject: [PATCH 041/112] refactor: after review --- src/Market.sol | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index cb2330cc5..0bedecd3b 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -186,21 +186,18 @@ contract Blue { require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); + // The size of the bonus is the proportion alpha of 1 / LLTV - 1 uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); - uint borrowPrice = market.borrowableOracle.price(); - uint collatPrice = market.collateralOracle.price(); + uint borrowablePrice = market.borrowableOracle.price(); + uint collateralPrice = market.collateralOracle.price(); seized = maxSeized.min(collateral[id][borrower]); - repaid = seized.wMul(collatPrice).wDiv(incentive).wDiv(borrowPrice); + repaid = seized.wMul(collateralPrice).wDiv(incentive).wDiv(borrowablePrice); uint priorBorrowShares = borrowShare[id][borrower]; uint priorBorrow = priorBorrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]); if (repaid > priorBorrow) { repaid = priorBorrow; - seized = repaid.wDiv(collatPrice).wMul(incentive).wMul(borrowPrice); + seized = repaid.wDiv(collateralPrice).wMul(incentive).wMul(borrowablePrice); } - uint shares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); - - // Keep this next computation outside of the if-then-else. - uint newTotalSupply = totalSupply[id] + repaid - priorBorrow; uint newCollateral = collateral[id][borrower] - seized; if (newCollateral == 0) { @@ -208,11 +205,12 @@ contract Blue { totalBorrowShares[id] -= priorBorrowShares; borrowShare[id][borrower] = 0; // Realize the bad debt. - totalSupply[id] = newTotalSupply; + totalSupply[id] -= priorBorrow - repaid; } else { + uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); totalBorrow[id] -= repaid; - totalBorrowShares[id] -= shares; - borrowShare[id][borrower] -= shares; + totalBorrowShares[id] -= repaidShares; + borrowShare[id][borrower] -= repaidShares; } collateral[id][borrower] = newCollateral; From c56ba57e0b2e0773042d4a7e613ad764d13905ab Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 4 Jul 2023 15:39:55 +0200 Subject: [PATCH 042/112] refactor: simplify isHealthy --- src/Market.sol | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 0bedecd3b..6effff99c 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -238,14 +238,12 @@ contract Blue { // Health check. function isHealthy(Market calldata market, Id id, address user) private view returns (bool) { - if (borrowShare[id][user] > 0) { - // totalBorrowShares[id] > 0 because borrowShare[id][user] > 0. - uint borrowValue = borrowShare[id][user].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul( - IOracle(market.borrowableOracle).price() - ); - uint collateralValue = collateral[id][user].wMul(IOracle(market.collateralOracle).price()); - return collateralValue.wMul(market.lLTV) >= borrowValue; - } - return true; + uint borrowShares = borrowShare[id][user]; + // totalBorrowShares[id] > 0 when borrowShares > 0. + uint borrowValue = borrowShares > 0 + ? borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price()) + : 0; + uint collateralValue = collateral[id][user].wMul(market.collateralOracle.price()); + return collateralValue.wMul(market.lLTV) >= borrowValue; } } From 797011cd2c4cb941a22fe840faab207164dc7428 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 4 Jul 2023 17:52:06 +0200 Subject: [PATCH 043/112] fix: from review --- test/hardhat/Market.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index a00e233ac..24c97589d 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -139,6 +139,8 @@ describe("Blue", () => { let maxSeize = closePositions ? constants.MaxUint256 : amount.div(2); market.lLTV = lltv; + // We use 2 different users to borrow from a bucket so that liquidations do not close a bucket completely. + // Consequently, we should only create the market on a particular LLTV once. if (i % 2 == 0) { await blue.connect(admin).createMarket(market); liquidationData.push({ @@ -181,9 +183,10 @@ describe("Blue", () => { let collat = await blue.collateral(id, user.address); assert( - closePositions || collat != BigNumber.from(0), + !closePositions || collat == BigNumber.from(0), "did not take the whole collateral when closing the position" ); + assert(closePositions || collat != BigNumber.from(0), "unexpectedly closed the position"); } }); }); From 0f52553e26c275fc4b680375f8990e5970f71051 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 4 Jul 2023 18:05:09 +0200 Subject: [PATCH 044/112] refactor: small cosmetic fixes --- test/hardhat/Market.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 24c97589d..51dea24ac 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -8,8 +8,9 @@ import { Blue, OracleMock, ERC20Mock } from "types"; const iterations = 500; -let nbLiquidations = 5; -let closePositions = false; +const closePositions = false; +const nbLiquidations = 5; +assert(2 * nbLiquidations + 1 < 20, "more liquidations than signers"); let seed = 42; @@ -41,6 +42,8 @@ interface Market { describe("Blue", () => { let signers: SignerWithAddress[]; + let admin: SignerWithAddress; + let liquidator: SignerWithAddress; let blue: Blue; let borrowable: ERC20Mock; @@ -50,9 +53,6 @@ describe("Blue", () => { let market: Market; let id: Buffer; - let admin: SignerWithAddress; - let liquidator: SignerWithAddress; - const initBalance = constants.MaxUint256.div(2); beforeEach(async () => { From 2972f3c6c9e347eddded8376fc0875858293132f Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Tue, 4 Jul 2023 18:08:21 +0200 Subject: [PATCH 045/112] refactor: clearer configuration setup --- test/hardhat/Market.spec.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Market.spec.ts index 51dea24ac..965dce9c5 100644 --- a/test/hardhat/Market.spec.ts +++ b/test/hardhat/Market.spec.ts @@ -7,10 +7,10 @@ import hre from "hardhat"; import { Blue, OracleMock, ERC20Mock } from "types"; const iterations = 500; - const closePositions = false; const nbLiquidations = 5; assert(2 * nbLiquidations + 1 < 20, "more liquidations than signers"); +const initBalance = constants.MaxUint256.div(2); let seed = 42; @@ -26,8 +26,8 @@ function random() { const abiCoder = new utils.AbiCoder(); function identifier(market: Market) { - let values = Object.values(market); - let encodedMarket = abiCoder.encode(["address", "address", "address", "address", "uint256"], values); + const values = Object.values(market); + const encodedMarket = abiCoder.encode(["address", "address", "address", "address", "uint256"], values); return Buffer.from(utils.keccak256(encodedMarket).slice(2), "hex"); } @@ -50,11 +50,10 @@ describe("Blue", () => { let collateral: ERC20Mock; let borrowableOracle: OracleMock; let collateralOracle: OracleMock; + let market: Market; let id: Buffer; - const initBalance = constants.MaxUint256.div(2); - beforeEach(async () => { signers = await hre.ethers.getSigners(); admin = signers[2 * nbLiquidations]; @@ -134,9 +133,9 @@ describe("Blue", () => { const tranche = Math.floor(1 + i / 2); const lltv = BigNumber.WAD.mul(tranche).div(nbLiquidations + 1); - let amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); - let borrowedAmount = amount.mul(lltv).div(BigNumber.WAD); - let maxSeize = closePositions ? constants.MaxUint256 : amount.div(2); + const amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); + const borrowedAmount = amount.mul(lltv).div(BigNumber.WAD); + const maxSeize = closePositions ? constants.MaxUint256 : amount.div(2); market.lLTV = lltv; // We use 2 different users to borrow from a bucket so that liquidations do not close a bucket completely. From dd002635ba5299e2fde89ba174b6b044b2040d2b Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 19:09:42 +0200 Subject: [PATCH 046/112] feat: simplify liquidation --- src/Market.sol | 43 +++++++++++++---------------------- test/forge/Market.t.sol | 50 ++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 6effff99c..e50d19ca8 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -174,45 +174,32 @@ contract Blue { // Liquidation. - function liquidate(Market calldata market, address borrower, uint maxSeized) - external - returns (uint seized, uint repaid) - { + function liquidate(Market calldata market, address borrower, uint seized) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(maxSeized > 0, "zero amount"); + require(seized > 0, "zero amount"); accrueInterests(id); require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); - // The size of the bonus is the proportion alpha of 1 / LLTV - 1 + // The size of the bonus is the proportion alpha of 1 / LLTV - 1. uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); - uint borrowablePrice = market.borrowableOracle.price(); - uint collateralPrice = market.collateralOracle.price(); - seized = maxSeized.min(collateral[id][borrower]); - repaid = seized.wMul(collateralPrice).wDiv(incentive).wDiv(borrowablePrice); - uint priorBorrowShares = borrowShare[id][borrower]; - uint priorBorrow = priorBorrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]); - if (repaid > priorBorrow) { - repaid = priorBorrow; - seized = repaid.wDiv(collateralPrice).wMul(incentive).wMul(borrowablePrice); - } + uint repaid = seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price()); + uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + + borrowShare[id][borrower] -= repaidShares; + totalBorrowShares[id] -= repaidShares; + totalBorrow[id] -= repaid; - uint newCollateral = collateral[id][borrower] - seized; - if (newCollateral == 0) { - totalBorrow[id] -= priorBorrow; - totalBorrowShares[id] -= priorBorrowShares; + collateral[id][borrower] -= seized; + + // Realize the bad debt if needed. + if (collateral[id][borrower] == 0) { + totalSupply[id] -= borrowShare[id][borrower].wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]); + totalBorrowShares[id] -= borrowShare[id][borrower]; borrowShare[id][borrower] = 0; - // Realize the bad debt. - totalSupply[id] -= priorBorrow - repaid; - } else { - uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); - totalBorrow[id] -= repaid; - totalBorrowShares[id] -= repaidShares; - borrowShare[id][borrower] -= repaidShares; } - collateral[id][borrower] = newCollateral; market.collateralAsset.safeTransfer(msg.sender, seized); market.borrowableAsset.safeTransferFrom(msg.sender, address(this), repaid); diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 602e1f654..680731ef9 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -53,6 +53,10 @@ contract BlueTest is Test { borrowableAsset.approve(address(blue), type(uint).max); collateralAsset.approve(address(blue), type(uint).max); vm.stopPrank(); + vm.startPrank(liquidator); + borrowableAsset.approve(address(blue), type(uint).max); + collateralAsset.approve(address(blue), type(uint).max); + vm.stopPrank(); } // To move to a test utils file later. @@ -244,7 +248,8 @@ contract BlueTest is Test { uint amountCollateral = amountLent; uint borrowingPower = amountCollateral.wMul(lLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); - uint maxCollat = amountCollateral.wMul(lLTV); + uint toSeize = amountCollateral.wMul(lLTV); + uint incentive = WAD + alpha.wMul(WAD.wDiv(lLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(borrower, amountCollateral); @@ -265,18 +270,17 @@ contract BlueTest is Test { uint liquidatorNetworthBefore = networth(liquidator); // Liquidate - vm.startPrank(liquidator); - borrowableAsset.approve(address(blue), type(uint).max); - (uint seized, uint repaid) = blue.liquidate(market, borrower, maxCollat); - vm.stopPrank(); + vm.prank(liquidator); + blue.liquidate(market, borrower, toSeize); uint liquidatorNetworthAfter = networth(liquidator); - assertGt(liquidatorNetworthAfter, liquidatorNetworthBefore, "liquidator's networth"); - assertGt(seized, 0, "collateral seized"); - assertGt(repaid, 0, "borrow repaid"); - assertApproxEqAbs(borrowBalance(borrower), amountBorrowed - repaid, 100, "collateral balance borrower"); - assertApproxEqAbs(blue.collateral(id, borrower), amountCollateral - seized, 100, "collateral balance borrower"); + uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); + uint expectedNetWorthAfter = liquidatorNetworthBefore + toSeize.wMul(collateralOracle.price()) + - expectedRepaid.wMul(borrowableOracle.price()); + assertEq(liquidatorNetworthAfter, expectedNetWorthAfter); + assertApproxEqAbs(borrowBalance(borrower), amountBorrowed - expectedRepaid, 100); + assertEq(blue.collateral(id, borrower), amountCollateral - toSeize); } function testRealizeBadDebt(uint amountLent) public { @@ -286,7 +290,8 @@ contract BlueTest is Test { uint amountCollateral = amountLent; uint borrowingPower = amountCollateral.wMul(lLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); - uint maxCollat = type(uint).max; + uint toSeize = amountCollateral; + uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(borrower, amountCollateral); @@ -307,21 +312,20 @@ contract BlueTest is Test { uint liquidatorNetworthBefore = networth(liquidator); // Liquidate - vm.startPrank(liquidator); - borrowableAsset.approve(address(blue), type(uint).max); - (uint seized, uint repaid) = blue.liquidate(market, borrower, maxCollat); - vm.stopPrank(); + vm.prank(liquidator); + blue.liquidate(market, borrower, toSeize); uint liquidatorNetworthAfter = networth(liquidator); - assertGt(liquidatorNetworthAfter, liquidatorNetworthBefore, "liquidator's networth"); - assertEq(seized, amountCollateral, "collateral seized"); - assertGt(repaid, 0, "borrow repaid"); - assertEq(borrowBalance(borrower), 0, "collateral balance borrower"); - assertEq(blue.collateral(id, borrower), 0, "collateral balance borrower"); - uint expectedBadDebt = amountBorrowed - repaid; - assertGt(expectedBadDebt, 0, "positive bad debt"); - assertApproxEqAbs(supplyBalance(address(this)), amountLent - expectedBadDebt, 10, "realized bad debt"); + uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); + uint expectedNetWorthAfter = liquidatorNetworthBefore + toSeize.wMul(collateralOracle.price()) + - expectedRepaid.wMul(borrowableOracle.price()); + assertEq(liquidatorNetworthAfter, expectedNetWorthAfter); + assertEq(borrowBalance(borrower), 0); + assertEq(blue.collateral(id, borrower), 0); + uint expectedBadDebt = amountBorrowed - expectedRepaid; + assertGt(expectedBadDebt, 0); + assertApproxEqAbs(supplyBalance(address(this)), amountLent - expectedBadDebt, 10); } function testTwoUsersSupply(uint firstAmount, uint secondAmount) public { From 3daf34fbfff1bb718c3e6ad915e53f67fa38ef82 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 19:17:03 +0200 Subject: [PATCH 047/112] test: improve liquidation testing --- test/forge/Market.t.sol | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index 680731ef9..aef09b747 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -61,7 +61,7 @@ contract BlueTest is Test { // To move to a test utils file later. - function networth(address user) internal view returns (uint) { + function netWorth(address user) internal view returns (uint) { uint collateralAssetValue = collateralAsset.balanceOf(user).wMul(collateralOracle.price()); uint borrowableAssetValue = borrowableAsset.balanceOf(user).wMul(borrowableOracle.price()); return collateralAssetValue + borrowableAssetValue; @@ -267,13 +267,13 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(2e18); - uint liquidatorNetworthBefore = networth(liquidator); + uint liquidatorNetworthBefore = netWorth(liquidator); // Liquidate vm.prank(liquidator); blue.liquidate(market, borrower, toSeize); - uint liquidatorNetworthAfter = networth(liquidator); + uint liquidatorNetworthAfter = netWorth(liquidator); uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); uint expectedNetWorthAfter = liquidatorNetworthBefore + toSeize.wMul(collateralOracle.price()) @@ -309,13 +309,13 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(100e18); - uint liquidatorNetworthBefore = networth(liquidator); + uint liquidatorNetworthBefore = netWorth(liquidator); // Liquidate vm.prank(liquidator); blue.liquidate(market, borrower, toSeize); - uint liquidatorNetworthAfter = networth(liquidator); + uint liquidatorNetworthAfter = netWorth(liquidator); uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); uint expectedNetWorthAfter = liquidatorNetworthBefore + toSeize.wMul(collateralOracle.price()) @@ -345,40 +345,29 @@ contract BlueTest is Test { assertEq(blue.supplyShare(id, borrower), secondAmount * 1e18 / firstAmount, "expected shares second user"); } - function testSupplyUnknownMarket(Market memory marketFuzz) public { + function testUnknownMarket(Market memory marketFuzz) public { vm.assume(neq(marketFuzz, market)); + vm.expectRevert("unknown market"); blue.supply(marketFuzz, 1); - } - function testWithdrawUnknownMarket(Market memory marketFuzz) public { - vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); blue.withdraw(marketFuzz, 1); - } - function testBorrowUnknownMarket(Market memory marketFuzz) public { - vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); blue.borrow(marketFuzz, 1); - } - function testRepayUnknownMarket(Market memory marketFuzz) public { - vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); blue.repay(marketFuzz, 1); - } - function testSupplyCollateralUnknownMarket(Market memory marketFuzz) public { - vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); blue.supplyCollateral(marketFuzz, 1); - } - function testWithdrawCollateralUnknownMarket(Market memory marketFuzz) public { - vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); blue.withdrawCollateral(marketFuzz, 1); + + vm.expectRevert("unknown market"); + blue.liquidate(marketFuzz, address(0), 1); } function testAmountZero() public { @@ -399,21 +388,24 @@ contract BlueTest is Test { vm.expectRevert("zero amount"); blue.withdrawCollateral(market, 0); + + vm.expectRevert("zero amount"); + blue.liquidate(market, address(0), 0); } - function testWithdrawEmptyMarket(uint amount) public { + function testEmptyMarketWithdraw(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); blue.withdraw(market, amount); } - function testRepayEmptyMarket(uint amount) public { + function testEmptyMarketRepay(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); blue.repay(market, amount); } - function testWithdrawCollateralEmptyMarket(uint amount) public { + function testEmptyMarketWithdrawCollateral(uint amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.arithmeticError); blue.withdrawCollateral(market, amount); From 78817cb5511940a5a0f89adef7b99e7c5199cde3 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 19:18:37 +0200 Subject: [PATCH 048/112] docs: minor comment improvement --- src/Market.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Market.sol b/src/Market.sol index e50d19ca8..d321e3cc0 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -183,7 +183,7 @@ contract Blue { require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); - // The size of the bonus is the proportion alpha of 1 / LLTV - 1. + // The liquidation incentive is 1 + alpha * (1 / LLTV - 1). uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); uint repaid = seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price()); uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); From d22f355ef467532602438887a07b91dde517e42e Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 19:50:25 +0200 Subject: [PATCH 049/112] fix: safeTransfer --- src/Market.sol | 4 ++-- src/interfaces/IERC20.sol | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index d321e3cc0..50dee219d 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -155,7 +155,7 @@ contract Blue { collateral[id][msg.sender] += amount; - market.collateralAsset.transferFrom(msg.sender, address(this), amount); + market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); } function withdrawCollateral(Market calldata market, uint amount) external { @@ -169,7 +169,7 @@ contract Blue { require(isHealthy(market, id, msg.sender), "not enough collateral"); - market.collateralAsset.transfer(msg.sender, amount); + market.collateralAsset.safeTransfer(msg.sender, amount); } // Liquidation. diff --git a/src/interfaces/IERC20.sol b/src/interfaces/IERC20.sol index 5a1f7b909..31f0cc95f 100644 --- a/src/interfaces/IERC20.sol +++ b/src/interfaces/IERC20.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; -interface IERC20 { - function transferFrom(address, address, uint) external; - function transfer(address, uint) external; -} +/// @dev Empty because we only call functions in assembly. It prevents calling +/// transfer (transferFrom) instead of safeTransfer (safeTransferFrom). +interface IERC20 {} From 2317096f772fd7687b29c85cfdef1d9f8e652643 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 08:55:28 +0200 Subject: [PATCH 050/112] perf: != 0 instead of < --- src/Market.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 6f7cb8148..39b5ca60a 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -62,7 +62,7 @@ contract Blue { function supply(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(amount > 0, "zero amount"); + require(amount != 0, "zero amount"); accrueInterests(id); @@ -83,7 +83,7 @@ contract Blue { function withdraw(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(amount > 0, "zero amount"); + require(amount != 0, "zero amount"); accrueInterests(id); @@ -103,7 +103,7 @@ contract Blue { function borrow(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(amount > 0, "zero amount"); + require(amount != 0, "zero amount"); accrueInterests(id); @@ -127,7 +127,7 @@ contract Blue { function repay(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(amount > 0, "zero amount"); + require(amount != 0, "zero amount"); accrueInterests(id); @@ -145,7 +145,7 @@ contract Blue { function supplyCollateral(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(amount > 0, "zero amount"); + require(amount != 0, "zero amount"); accrueInterests(id); @@ -157,7 +157,7 @@ contract Blue { function withdrawCollateral(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(amount > 0, "zero amount"); + require(amount != 0, "zero amount"); accrueInterests(id); From 0af912c77f92d4403bdacbecbf2d01046695f5e4 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 10:22:06 +0200 Subject: [PATCH 051/112] refactor: use helper id function to avoid redundant code --- src/Market.sol | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 6f7cb8148..b4b91ed2c 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -9,6 +9,7 @@ import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; // Market id. type Id is bytes32; +using {toId} for Id; // Market. struct Market { @@ -25,6 +26,10 @@ function irm(uint utilization) pure returns (uint) { return utilization / 365 days; } +function toId() pure returns (bytes32) { + return market.toId(); +} + contract Blue { using MathLib for uint; using SafeTransferLib for IERC20; @@ -51,7 +56,7 @@ contract Blue { // Markets management. function createMarket(Market calldata market) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id); @@ -60,7 +65,7 @@ contract Blue { // Supply management. function supply(Market calldata market, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -81,7 +86,7 @@ contract Blue { } function withdraw(Market calldata market, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -101,7 +106,7 @@ contract Blue { // Borrow management. function borrow(Market calldata market, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -125,7 +130,7 @@ contract Blue { } function repay(Market calldata market, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -143,7 +148,7 @@ contract Blue { // Collateral management. function supplyCollateral(Market calldata market, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); @@ -155,7 +160,7 @@ contract Blue { } function withdrawCollateral(Market calldata market, uint amount) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); From 5737a62a30b2ab360cc0def3c9fda5f84c087fa9 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 10:26:56 +0200 Subject: [PATCH 052/112] fix: correct function --- src/Market.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index b4b91ed2c..025c74bab 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -9,7 +9,6 @@ import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; // Market id. type Id is bytes32; -using {toId} for Id; // Market. struct Market { @@ -26,8 +25,9 @@ function irm(uint utilization) pure returns (uint) { return utilization / 365 days; } -function toId() pure returns (bytes32) { - return market.toId(); +using {toId} for Market; +function toId(Market calldata market) pure returns (Id) { + return Id.wrap(keccak256(abi.encode(market))); } contract Blue { From 6bed7771766738b6574299f012477efccd2c4e13 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Wed, 5 Jul 2023 10:38:43 +0200 Subject: [PATCH 053/112] test: minor improvements --- test/forge/Market.t.sol | 75 +++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/test/forge/Market.t.sol b/test/forge/Market.t.sol index aef09b747..5f547ecaf 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Market.t.sol @@ -86,7 +86,7 @@ contract BlueTest is Test { // Invariants function invariantLiquidity() public { - assertLe(blue.totalBorrow(id), blue.totalSupply(id)); + assertLe(blue.totalBorrow(id), blue.totalSupply(id), "liquidity"); } // Tests @@ -97,9 +97,9 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), amount); blue.supply(market, amount); - assertEq(blue.supplyShare(id, address(this)), 1e18); - assertEq(borrowableAsset.balanceOf(address(this)), 0); - assertEq(borrowableAsset.balanceOf(address(blue)), amount); + assertEq(blue.supplyShare(id, address(this)), 1e18, "supply share"); + assertEq(borrowableAsset.balanceOf(address(this)), 0, "lender balance"); + assertEq(borrowableAsset.balanceOf(address(blue)), amount, "blue balance"); } function testBorrow(uint amountLent, uint amountBorrowed) public { @@ -124,9 +124,9 @@ contract BlueTest is Test { vm.prank(borrower); blue.borrow(market, amountBorrowed); - assertEq(blue.borrowShare(id, borrower), 1e18); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed); - assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed); + assertEq(blue.borrowShare(id, borrower), 1e18, "borrow share"); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed, "borrower balance"); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed, "blue balance"); } function testWithdraw(uint amountLent, uint amountWithdrawn, uint amountBorrowed) public { @@ -153,9 +153,9 @@ contract BlueTest is Test { blue.withdraw(market, amountWithdrawn); - assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3); - assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn); - assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn); + assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3, "supply share"); + assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn, "this balance"); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn, "blue balance"); } function testCollateralRequirements( @@ -205,9 +205,9 @@ contract BlueTest is Test { blue.repay(market, amountRepaid); vm.stopPrank(); - assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid); - assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid); + assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share"); + assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid, "borrower balance"); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } function testSupplyCollateral(uint amount) public { @@ -216,9 +216,9 @@ contract BlueTest is Test { collateralAsset.setBalance(address(this), amount); blue.supplyCollateral(market, amount); - assertEq(blue.collateral(id, address(this)), amount); - assertEq(collateralAsset.balanceOf(address(this)), 0); - assertEq(collateralAsset.balanceOf(address(blue)), amount); + assertEq(blue.collateral(id, address(this)), amount, "collateral"); + assertEq(collateralAsset.balanceOf(address(this)), 0, "this balance"); + assertEq(collateralAsset.balanceOf(address(blue)), amount, "blue balance"); } function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn) public { @@ -236,9 +236,9 @@ contract BlueTest is Test { blue.withdrawCollateral(market, amountWithdrawn); - assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn); - assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn); - assertEq(collateralAsset.balanceOf(address(blue)), amountDeposited - amountWithdrawn); + assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn, "this collateral"); + assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn, "this balance"); + assertEq(collateralAsset.balanceOf(address(blue)), amountDeposited - amountWithdrawn, "blue balance"); } function testLiquidate(uint amountLent) public { @@ -267,20 +267,20 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(2e18); - uint liquidatorNetworthBefore = netWorth(liquidator); + uint liquidatorNetWorthBefore = netWorth(liquidator); // Liquidate vm.prank(liquidator); blue.liquidate(market, borrower, toSeize); - uint liquidatorNetworthAfter = netWorth(liquidator); + uint liquidatorNetWorthAfter = netWorth(liquidator); uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); - uint expectedNetWorthAfter = liquidatorNetworthBefore + toSeize.wMul(collateralOracle.price()) + uint expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) - expectedRepaid.wMul(borrowableOracle.price()); - assertEq(liquidatorNetworthAfter, expectedNetWorthAfter); - assertApproxEqAbs(borrowBalance(borrower), amountBorrowed - expectedRepaid, 100); - assertEq(blue.collateral(id, borrower), amountCollateral - toSeize); + assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "liquidator net worth"); + assertApproxEqAbs(borrowBalance(borrower), amountBorrowed - expectedRepaid, 100, "borrower balance"); + assertEq(blue.collateral(id, borrower), amountCollateral - toSeize, "borrower collateral"); } function testRealizeBadDebt(uint amountLent) public { @@ -309,23 +309,23 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(100e18); - uint liquidatorNetworthBefore = netWorth(liquidator); + uint liquidatorNetWorthBefore = netWorth(liquidator); // Liquidate vm.prank(liquidator); blue.liquidate(market, borrower, toSeize); - uint liquidatorNetworthAfter = netWorth(liquidator); + uint liquidatorNetWorthAfter = netWorth(liquidator); uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); - uint expectedNetWorthAfter = liquidatorNetworthBefore + toSeize.wMul(collateralOracle.price()) + uint expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) - expectedRepaid.wMul(borrowableOracle.price()); - assertEq(liquidatorNetworthAfter, expectedNetWorthAfter); - assertEq(borrowBalance(borrower), 0); - assertEq(blue.collateral(id, borrower), 0); + assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "liquidator net worth"); + assertEq(borrowBalance(borrower), 0, "borrower balance"); + assertEq(blue.collateral(id, borrower), 0, "borrower collateral"); uint expectedBadDebt = amountBorrowed - expectedRepaid; - assertGt(expectedBadDebt, 0); - assertApproxEqAbs(supplyBalance(address(this)), amountLent - expectedBadDebt, 10); + assertGt(expectedBadDebt, 0, "bad debt"); + assertApproxEqAbs(supplyBalance(address(this)), amountLent - expectedBadDebt, 10, "lender supply balance"); } function testTwoUsersSupply(uint firstAmount, uint secondAmount) public { @@ -393,20 +393,15 @@ contract BlueTest is Test { blue.liquidate(market, address(0), 0); } - function testEmptyMarketWithdraw(uint amount) public { + function testEmptyMarket(uint amount) public { vm.assume(amount > 0); + vm.expectRevert(stdError.divisionError); blue.withdraw(market, amount); - } - function testEmptyMarketRepay(uint amount) public { - vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); blue.repay(market, amount); - } - function testEmptyMarketWithdrawCollateral(uint amount) public { - vm.assume(amount > 0); vm.expectRevert(stdError.arithmeticError); blue.withdrawCollateral(market, amount); } From f438dd871e0d0fdde0ede7d80460f07fb1f7f9cf Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 4 Jul 2023 13:44:50 +0200 Subject: [PATCH 054/112] chore: files renaming --- src/{Market.sol => Blue.sol} | 0 test/forge/{Market.t.sol => Blue.t.sol} | 2 +- test/hardhat/{Market.spec.ts => Blue.spec.ts} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/{Market.sol => Blue.sol} (100%) rename test/forge/{Market.t.sol => Blue.t.sol} (99%) rename test/hardhat/{Market.spec.ts => Blue.spec.ts} (100%) diff --git a/src/Market.sol b/src/Blue.sol similarity index 100% rename from src/Market.sol rename to src/Blue.sol diff --git a/test/forge/Market.t.sol b/test/forge/Blue.t.sol similarity index 99% rename from test/forge/Market.t.sol rename to test/forge/Blue.t.sol index 5f547ecaf..d814c7f97 100644 --- a/test/forge/Market.t.sol +++ b/test/forge/Blue.t.sol @@ -7,7 +7,7 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -import "src/Market.sol"; +import "src/Blue.sol"; import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; diff --git a/test/hardhat/Market.spec.ts b/test/hardhat/Blue.spec.ts similarity index 100% rename from test/hardhat/Market.spec.ts rename to test/hardhat/Blue.spec.ts From 0e3b83284f929144e342f06b4b3880337a417a21 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 11:00:37 +0200 Subject: [PATCH 055/112] fix: pin hh to 2.15 --- package.json | 2 +- yarn.lock | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b7becd9d0..4381200e5 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@types/node": "^20.2.5", "chai": "^4.3.7", "dotenv": "^16.1.3", - "hardhat": "^2.14.1", + "hardhat": "2.15", "hardhat-deal": "^1.3.0", "hardhat-gas-reporter": "^1.0.9", "hardhat-tracer": "2.3.2", diff --git a/yarn.lock b/yarn.lock index 88f75030b..9fe12863f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2785,10 +2785,10 @@ hardhat-tracer@2.3.2: chalk "^4.1.2" ethers "^5.6.1" -hardhat@^2.14.1: - version "2.16.0" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.16.0.tgz#c5611d433416b31f6ce92f733b1f1b5236ad6230" - integrity sha512-7VQEJPQRAZdtrYUZaU9GgCpP3MBNy/pTdscARNJQMWKj5C+R7V32G5uIZKIqZ4QiqXa6CBfxxe+G+ahxUbHZHA== +hardhat@2.15: + version "2.15.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.15.0.tgz#0cacb2b44c4c4651aa8ab649fef12804848b0267" + integrity sha512-cC9tM/N10YaES04zPOp7yR13iX3YibqaNmi0//Ep40Nt9ELIJx3kFpQmucur0PAIfXYpGnw5RuXHNLkxpnVHEw== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -2829,6 +2829,7 @@ hardhat@^2.14.1: mnemonist "^0.38.0" mocha "^10.0.0" p-map "^4.0.0" + qs "^6.7.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" @@ -4115,7 +4116,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -qs@^6.4.0: +qs@^6.4.0, qs@^6.7.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== From 1ae8880036c1f0fa23fb839477c7eaa63c338e43 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 11:11:10 +0200 Subject: [PATCH 056/112] perf: same for isHealthy and liquidate --- src/Market.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Market.sol b/src/Market.sol index 14a12d45a..fe1b2b1de 100644 --- a/src/Market.sol +++ b/src/Market.sol @@ -177,7 +177,7 @@ contract Blue { function liquidate(Market calldata market, address borrower, uint seized) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); - require(seized > 0, "zero amount"); + require(seized != 0, "zero amount"); accrueInterests(id); @@ -227,7 +227,7 @@ contract Blue { function isHealthy(Market calldata market, Id id, address user) private view returns (bool) { uint borrowShares = borrowShare[id][user]; // totalBorrowShares[id] > 0 when borrowShares > 0. - uint borrowValue = borrowShares > 0 + uint borrowValue = borrowShares != 0 ? borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price()) : 0; uint collateralValue = collateral[id][user].wMul(market.collateralOracle.price()); From c0282d862224652cb27df6af9d4a32c302b72b9d Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 11:18:10 +0200 Subject: [PATCH 057/112] feat: id for liquidate + move function upward --- src/Blue.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 13f3c4ae5..0235c69e7 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -23,17 +23,17 @@ struct Market { uint lLTV; } +using {toId} for Market; +function toId(Market calldata market) pure returns (Id) { + return Id.wrap(keccak256(abi.encode(market))); +} + function irm(uint utilization) pure returns (uint) { // Divide by the number of seconds in a year. // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. return utilization / 365 days; } -using {toId} for Market; -function toId(Market calldata market) pure returns (Id) { - return Id.wrap(keccak256(abi.encode(market))); -} - contract Blue { using MathLib for uint; using SafeTransferLib for IERC20; @@ -180,7 +180,7 @@ contract Blue { // Liquidation. function liquidate(Market calldata market, address borrower, uint seized) external { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(seized > 0, "zero amount"); From c20570ba1bc44f035b65f6d1b78609dee3b0e8f7 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 11:56:20 +0200 Subject: [PATCH 058/112] feat: add owner to contract --- src/Blue.sol | 6 +++++- src/dependencies/Owned.sol | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/dependencies/Owned.sol diff --git a/src/Blue.sol b/src/Blue.sol index 50dee219d..306e93435 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -7,6 +7,8 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; +import {Owned} from "src/dependencies/Owned.sol"; + uint constant WAD = 1e18; uint constant alpha = 0.5e18; @@ -29,7 +31,7 @@ function irm(uint utilization) pure returns (uint) { return utilization / 365 days; } -contract Blue { +contract Blue is Owned { using MathLib for uint; using SafeTransferLib for IERC20; @@ -52,6 +54,8 @@ contract Blue { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; + constructor(address owner) Owned(owner) {} + // Markets management. function createMarket(Market calldata market) external { diff --git a/src/dependencies/Owned.sol b/src/dependencies/Owned.sol new file mode 100644 index 000000000..e82b44d83 --- /dev/null +++ b/src/dependencies/Owned.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Simple single owner authorization mixin. +/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) +abstract contract Owned { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event OwnershipTransferred(address indexed user, address indexed newOwner); + + /*////////////////////////////////////////////////////////////// + OWNERSHIP STORAGE + //////////////////////////////////////////////////////////////*/ + + address public owner; + + modifier onlyOwner() virtual { + require(msg.sender == owner, "UNAUTHORIZED"); + + _; + } + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(address _owner) { + owner = _owner; + + emit OwnershipTransferred(address(0), _owner); + } + + /*////////////////////////////////////////////////////////////// + OWNERSHIP LOGIC + //////////////////////////////////////////////////////////////*/ + + function transferOwnership(address newOwner) public virtual onlyOwner { + owner = newOwner; + + emit OwnershipTransferred(msg.sender, newOwner); + } +} From f877eb5587a10d87c5003ef7a9a43f0d8752dbd5 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 12:34:38 +0200 Subject: [PATCH 059/112] fix: deployment --- test/forge/Blue.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index d814c7f97..b1ab80dc6 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -28,7 +28,7 @@ contract BlueTest is Test { function setUp() public { // Create Blue. - blue = new Blue(); + blue = new Blue(msg.sender); // List a market. borrowableAsset = new ERC20("borrowable", "B", 18); From 0babf30584ef3a5c8dc6c9314dece31f4d560195 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 5 Jul 2023 15:02:02 +0200 Subject: [PATCH 060/112] chore: revert now unnecessary changes to yarn.lock --- yarn.lock | 195 +++++++++++++++++++++++++----------------------------- 1 file changed, 90 insertions(+), 105 deletions(-) diff --git a/yarn.lock b/yarn.lock index 652b4b9a3..88f75030b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== +"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.22.5" "@babel/generator@7.17.7": version "7.17.7" @@ -19,74 +19,74 @@ source-map "^0.5.0" "@babel/generator@^7.17.3": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" - integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== dependencies: - "@babel/types" "^7.22.3" + "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-environment-visitor@^7.16.7": - version "7.22.1" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" - integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== "@babel/helper-function-name@^7.16.7": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/helper-hoist-variables@^7.16.7": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" "@babel/helper-split-export-declaration@^7.16.7": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" + integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" - integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-validator-identifier" "^7.22.5" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.17.3", "@babel/parser@^7.20.5", "@babel/parser@^7.21.9": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" - integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== +"@babel/parser@^7.17.3", "@babel/parser@^7.20.5", "@babel/parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" + integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== -"@babel/template@^7.20.7": - version "7.21.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" - integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/parser" "^7.21.9" - "@babel/types" "^7.21.5" + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/traverse@7.17.3": version "7.17.3" @@ -112,13 +112,13 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.17.0", "@babel/types@^7.18.6", "@babel/types@^7.21.0", "@babel/types@^7.21.5", "@babel/types@^7.22.3": - version "7.22.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" - integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== +"@babel/types@^7.17.0", "@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== dependencies: - "@babel/helper-string-parser" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@chainsafe/as-sha256@^0.3.1": @@ -567,14 +567,6 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" -"@morgan-stanley/ts-mocking-bird@^0.6.2": - version "0.6.4" - resolved "https://registry.yarnpkg.com/@morgan-stanley/ts-mocking-bird/-/ts-mocking-bird-0.6.4.tgz#2e4b60d42957bab3b50b67dbf14c3da2f62a39f7" - integrity sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA== - dependencies: - lodash "^4.17.16" - uuid "^7.0.3" - "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" @@ -1049,9 +1041,9 @@ integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== "@types/node@*", "@types/node@^20.2.5": - version "20.2.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" - integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== + version "20.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" + integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== "@types/node@^10.0.3": version "10.17.60" @@ -1071,9 +1063,9 @@ "@types/node" "*" "@types/prettier@^2.1.1": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" - integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/qs@^6.2.31": version "6.9.7" @@ -1131,9 +1123,9 @@ acorn-walk@^8.1.1: integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^8.4.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== address@^1.0.1: version "1.2.2" @@ -1983,9 +1975,9 @@ dir-glob@^3.0.1: path-type "^4.0.0" dotenv@^16.1.3: - version "16.1.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.1.3.tgz#0c67e90d0ddb48d08c570888f709b41844928210" - integrity sha512-FYssxsmCTtKL72fGBSvb1K9dRz0/VZeWqFme/vSb7r7323x4CRaHu4LvQ5JG3+s6yt2YPbBrkpiEODktfyjI9A== + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== eastasianwidth@^0.2.0: version "0.2.0" @@ -2772,9 +2764,9 @@ har-validator@~5.1.3: har-schema "^2.0.0" hardhat-deal@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/hardhat-deal/-/hardhat-deal-1.3.0.tgz#06fbbaafc78e3dce07159049064bc1cb551ca014" - integrity sha512-jnD2nryxA+GSZ4RsuKAoIQYhGK1+U0gjMS4SZdCz5Wxmfdv4Qgk05lqQxsR6Ko5nMG27uMJAWPVHjYdvhaRH4w== + version "1.4.0" + resolved "https://registry.yarnpkg.com/hardhat-deal/-/hardhat-deal-1.4.0.tgz#3f42f52603c1f9eeb7e583ca7261478d17b875a4" + integrity sha512-8+p0c7u3RsRWf5mzAAUbJTq11HAJJb+tj6AcM9w4ObN1LTMBouQ7D6sc3zQwLP8dddYbA7z0wrkp+zn61cPxCg== hardhat-gas-reporter@^1.0.9: version "1.0.9" @@ -2794,9 +2786,9 @@ hardhat-tracer@2.3.2: ethers "^5.6.1" hardhat@^2.14.1: - version "2.14.1" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.14.1.tgz#4dd252717f4987d8221c4f6fd08233b7f4251fd8" - integrity sha512-H3Qp/UKyQGmPDDBSfMoSyH18rRnac90rsb0LNer+sKe6at6rxLe4D5j+M+1icqZQF02iLPjNRwc/PA8OPf757A== + version "2.16.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.16.0.tgz#c5611d433416b31f6ce92f733b1f1b5236ad6230" + integrity sha512-7VQEJPQRAZdtrYUZaU9GgCpP3MBNy/pTdscARNJQMWKj5C+R7V32G5uIZKIqZ4QiqXa6CBfxxe+G+ahxUbHZHA== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -2837,7 +2829,6 @@ hardhat@^2.14.1: mnemonist "^0.38.0" mocha "^10.0.0" p-map "^4.0.0" - qs "^6.7.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" @@ -3262,9 +3253,9 @@ javascript-natural-sort@0.7.1: integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== js-sdsl@^4.1.4: - version "4.4.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" - integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== + version "4.4.1" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.1.tgz#9e3c7b566d8d9a7e1fe8fc26d00b5ab0f8918ab3" + integrity sha512-6Gsx8R0RucyePbWqPssR8DyfuXmLBooYN5cZFZKjHGnQuaf7pEzhtpceagJxVu4LqhYY5EYA7nko3FmeHZ1KbA== js-sha3@0.5.7: version "0.5.7" @@ -3483,7 +3474,7 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16, lodash@^4.17.19, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4124,7 +4115,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -qs@^6.4.0, qs@^6.7.0: +qs@^6.4.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -4469,9 +4460,9 @@ semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.4: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" @@ -4600,9 +4591,9 @@ solc@0.7.3: tmp "0.0.33" solidity-coverage@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.2.tgz#bc39604ab7ce0a3fa7767b126b44191830c07813" - integrity sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ== + version "0.8.3" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.3.tgz#72ce51e5ca9ea1182bbf6085eb1cf526f0603b52" + integrity sha512-hbcNgj5z8zzgTlnp4F0pXiqj1v5ua8P4DH5i9cWOBtFPfUuIohLoXu5WiAixexWmpKVjyxXqupnu/mPb4IGr7Q== dependencies: "@ethersproject/abi" "^5.0.9" "@solidity-parser/parser" "^0.14.1" @@ -4949,11 +4940,10 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: punycode "^2.1.1" ts-command-line-args@^2.2.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz#7eeed3a6937b2612ea08a0794cf9d43fbbea89c4" - integrity sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw== + version "2.5.1" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" + integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== dependencies: - "@morgan-stanley/ts-mocking-bird" "^0.6.2" chalk "^4.1.0" command-line-args "^5.1.1" command-line-usage "^6.1.0" @@ -4989,9 +4979,9 @@ tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0: - version "2.5.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" - integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== tsort@0.0.1: version "0.0.1" @@ -5151,11 +5141,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" From 05b8db618b9e8fedd9da6597b957ff26c8271fd3 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 15:24:18 +0200 Subject: [PATCH 061/112] feat: update file --- src/Blue.sol | 6 +++--- src/Ownable.sol | 39 +++++++++++++++++++++++++++++++++ src/dependencies/Owned.sol | 44 -------------------------------------- 3 files changed, 42 insertions(+), 47 deletions(-) create mode 100644 src/Ownable.sol delete mode 100644 src/dependencies/Owned.sol diff --git a/src/Blue.sol b/src/Blue.sol index 306e93435..834fe13b2 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -7,7 +7,7 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; -import {Owned} from "src/dependencies/Owned.sol"; +import {Ownable} from "src/Ownable.sol"; uint constant WAD = 1e18; @@ -31,7 +31,7 @@ function irm(uint utilization) pure returns (uint) { return utilization / 365 days; } -contract Blue is Owned { +contract Blue is Ownable { using MathLib for uint; using SafeTransferLib for IERC20; @@ -54,7 +54,7 @@ contract Blue is Owned { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; - constructor(address owner) Owned(owner) {} + constructor(address owner) Ownable(owner) {} // Markets management. diff --git a/src/Ownable.sol b/src/Ownable.sol new file mode 100644 index 000000000..f664124b4 --- /dev/null +++ b/src/Ownable.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @title Ownable +/// @author Morpho Labs +/// @custom:contact security@morpho.xyz +/// @dev Greatly inspired by Solmate and OZ implementations. +abstract contract Ownable { + /* EVENTS */ + + event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); + + /* STORAGE */ + + address public owner; + + /* MODIFIERS */ + + modifier onlyOwner() virtual { + require(msg.sender == owner, "not owner"); + _; + } + + /* CONSTRUCTOR */ + + constructor(address newOwner) { + owner = newOwner; + + emit OwnershipTransferred(address(0), newOwner); + } + + /* PUBLIC */ + + function transferOwnership(address newOwner) public virtual onlyOwner { + owner = newOwner; + + emit OwnershipTransferred(msg.sender, newOwner); + } +} diff --git a/src/dependencies/Owned.sol b/src/dependencies/Owned.sol deleted file mode 100644 index e82b44d83..000000000 --- a/src/dependencies/Owned.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Simple single owner authorization mixin. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) -abstract contract Owned { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event OwnershipTransferred(address indexed user, address indexed newOwner); - - /*////////////////////////////////////////////////////////////// - OWNERSHIP STORAGE - //////////////////////////////////////////////////////////////*/ - - address public owner; - - modifier onlyOwner() virtual { - require(msg.sender == owner, "UNAUTHORIZED"); - - _; - } - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner) { - owner = _owner; - - emit OwnershipTransferred(address(0), _owner); - } - - /*////////////////////////////////////////////////////////////// - OWNERSHIP LOGIC - //////////////////////////////////////////////////////////////*/ - - function transferOwnership(address newOwner) public virtual onlyOwner { - owner = newOwner; - - emit OwnershipTransferred(msg.sender, newOwner); - } -} From 70cb8b026c3a27a4bc7aa78fbc27b72db804a70e Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 15:27:39 +0200 Subject: [PATCH 062/112] test: add test owner --- test/forge/Blue.t.sol | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index b1ab80dc6..e31bbd849 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -91,6 +91,30 @@ contract BlueTest is Test { // Tests + function testOwner(address owner) public { + Blue blue2 = new Blue(owner); + + assertEq(blue2.owner(), owner, "owner"); + } + + function testTransferOwnership(address oldOwner, address newOwner) public { + Blue blue2 = new Blue(oldOwner); + + vm.prank(oldOwner); + blue2.transferOwnership(newOwner); + assertEq(blue2.owner(), newOwner, "owner"); + } + + function testTransferOwnershipWhenNotOwner(address attacker, address newOwner) public { + vm.assume(attacker != address(0xdead)); + + Blue blue2 = new Blue(address(0xdead)); + + vm.prank(attacker); + vm.expectRevert("not owner"); + blue2.transferOwnership(newOwner); + } + function testSupply(uint amount) public { amount = bound(amount, 1, 2 ** 64); From 2dc559359ef41f2e42f8118838c1c891a38b41e5 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 15:37:59 +0200 Subject: [PATCH 063/112] perf: remove accrue interest from supplyCollateral function --- src/Blue.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 50dee219d..8d384385e 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -151,8 +151,6 @@ contract Blue { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); - collateral[id][msg.sender] += amount; market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); From d9cb441a7b309a9dcd423fac804ec71c1be9ae66 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 16:30:58 +0200 Subject: [PATCH 064/112] docs: add comment --- src/Blue.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blue.sol b/src/Blue.sol index 8d384385e..5554ee7f5 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -146,6 +146,7 @@ contract Blue { // Collateral management. + /// @dev The interests are not accrued since no health check is required when supplying collateral. function supplyCollateral(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); From 820079767cb7441ab73c43093d8e6508a68a8998 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 16:51:55 +0200 Subject: [PATCH 065/112] refactor: capital letters for constants --- src/Blue.sol | 7 +++---- test/forge/Blue.t.sol | 16 +++++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 50dee219d..891af594b 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -8,8 +8,7 @@ import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; uint constant WAD = 1e18; - -uint constant alpha = 0.5e18; +uint constant ALPHA = 0.5e18; // Market id. type Id is bytes32; @@ -183,8 +182,8 @@ contract Blue { require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); - // The liquidation incentive is 1 + alpha * (1 / LLTV - 1). - uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); + // The liquidation incentive is 1 + ALPHA * (1 / LLTV - 1). + uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lLTV) - WAD); uint repaid = seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price()); uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index d814c7f97..753372473 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -153,9 +153,13 @@ contract BlueTest is Test { blue.withdraw(market, amountWithdrawn); - assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3, "supply share"); + assertApproxEqAbs( + blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3, "supply share" + ); assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn, "this balance"); - assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn, "blue balance"); + assertEq( + borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn, "blue balance" + ); } function testCollateralRequirements( @@ -205,7 +209,9 @@ contract BlueTest is Test { blue.repay(market, amountRepaid); vm.stopPrank(); - assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share"); + assertApproxEqAbs( + blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share" + ); assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid, "borrower balance"); assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } @@ -249,7 +255,7 @@ contract BlueTest is Test { uint borrowingPower = amountCollateral.wMul(lLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); uint toSeize = amountCollateral.wMul(lLTV); - uint incentive = WAD + alpha.wMul(WAD.wDiv(lLTV) - WAD); + uint incentive = WAD + ALPHA.wMul(WAD.wDiv(lLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(borrower, amountCollateral); @@ -291,7 +297,7 @@ contract BlueTest is Test { uint borrowingPower = amountCollateral.wMul(lLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); uint toSeize = amountCollateral; - uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); + uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(borrower, amountCollateral); From a1e6fbe74cc5e60fb5d05d5c7a10393f8ca38518 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Wed, 5 Jul 2023 17:58:23 +0200 Subject: [PATCH 066/112] refactor: simplify isHealthy --- src/Blue.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index fe1b2b1de..466f2ef71 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -226,10 +226,10 @@ contract Blue { function isHealthy(Market calldata market, Id id, address user) private view returns (bool) { uint borrowShares = borrowShare[id][user]; + if (borrowShares == 0) return true; // totalBorrowShares[id] > 0 when borrowShares > 0. - uint borrowValue = borrowShares != 0 - ? borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price()) - : 0; + uint borrowValue = + borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price()); uint collateralValue = collateral[id][user].wMul(market.collateralOracle.price()); return collateralValue.wMul(market.lLTV) >= borrowValue; } From 1b481195e07ec35f55ec31065bca23643a9a3b93 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 17:58:49 +0200 Subject: [PATCH 067/112] feat: add IRM whitelist --- src/Blue.sol | 35 +++++++++++++++++++---------------- src/Ownable.sol | 2 ++ src/interfaces/IIRM.sol | 6 ++++++ src/mocks/IRMMock.sol | 10 ++++++++++ test/forge/Blue.t.sol | 8 ++++++-- 5 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 src/interfaces/IIRM.sol create mode 100644 src/mocks/IRMMock.sol diff --git a/src/Blue.sol b/src/Blue.sol index 834fe13b2..3b2d9922a 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; +import {IIRM} from "src/interfaces/IIRM.sol"; import {IERC20} from "src/interfaces/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; @@ -22,15 +23,10 @@ struct Market { IERC20 collateralAsset; IOracle borrowableOracle; IOracle collateralOracle; + address irm; uint lLTV; } -function irm(uint utilization) pure returns (uint) { - // Divide by the number of seconds in a year. - // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. - return utilization / 365 days; -} - contract Blue is Ownable { using MathLib for uint; using SafeTransferLib for IERC20; @@ -53,6 +49,8 @@ contract Blue is Ownable { mapping(Id => uint) public totalBorrowShares; // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; + // IRM whitelist. + mapping(address => bool) public whitelistedIRMs; constructor(address owner) Ownable(owner) {} @@ -60,9 +58,14 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); + require(whitelistedIRMs[market.irm], "IRM not whitelisted"); require(lastUpdate[id] == 0, "market already exists"); - accrueInterests(id); + accrueInterests(id, market.irm); + } + + function whitelistIRM(address irm) external onlyOwner { + whitelistedIRMs[irm] = !whitelistedIRMs[irm]; } // Supply management. @@ -72,7 +75,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); if (totalSupply[id] == 0) { supplyShare[id][msg.sender] = WAD; @@ -93,7 +96,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); supplyShare[id][msg.sender] -= shares; @@ -113,7 +116,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); if (totalBorrow[id] == 0) { borrowShare[id][msg.sender] = WAD; @@ -137,7 +140,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); uint shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); borrowShare[id][msg.sender] -= shares; @@ -155,7 +158,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); collateral[id][msg.sender] += amount; @@ -167,7 +170,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); collateral[id][msg.sender] -= amount; @@ -183,7 +186,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(seized > 0, "zero amount"); - accrueInterests(id); + accrueInterests(id, market.irm); require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); @@ -211,13 +214,13 @@ contract Blue is Ownable { // Interests management. - function accrueInterests(Id id) private { + function accrueInterests(Id id, address irm) private { uint marketTotalSupply = totalSupply[id]; if (marketTotalSupply != 0) { uint marketTotalBorrow = totalBorrow[id]; uint utilization = marketTotalBorrow.wDiv(marketTotalSupply); - uint borrowRate = irm(utilization); + uint borrowRate = IIRM(irm).rate(utilization); uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; diff --git a/src/Ownable.sol b/src/Ownable.sol index f664124b4..0de7d2d63 100644 --- a/src/Ownable.sol +++ b/src/Ownable.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; +import "forge-std/console.sol"; + /// @title Ownable /// @author Morpho Labs /// @custom:contact security@morpho.xyz diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIRM.sol new file mode 100644 index 000000000..1feb71fe0 --- /dev/null +++ b/src/interfaces/IIRM.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.5.0; + +interface IIRM { + function rate(uint utilization) external pure returns (uint); +} diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol new file mode 100644 index 000000000..95839519f --- /dev/null +++ b/src/mocks/IRMMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.20; + +contract IRMMock { + function rate(uint utilization) external pure returns (uint) { + // Divide by the number of seconds in a year. + // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. + return utilization / 365 days; + } +} diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index e31bbd849..5c1809e78 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -10,6 +10,7 @@ import "forge-std/console.sol"; import "src/Blue.sol"; import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; +import {IRMMock as IRM} from "src/mocks/IRMMock.sol"; contract BlueTest is Test { using MathLib for uint; @@ -23,20 +24,23 @@ contract BlueTest is Test { ERC20 private collateralAsset; Oracle private borrowableOracle; Oracle private collateralOracle; + IRM private irm; Market public market; Id public id; function setUp() public { // Create Blue. - blue = new Blue(msg.sender); + blue = new Blue(address(this)); // List a market. borrowableAsset = new ERC20("borrowable", "B", 18); collateralAsset = new ERC20("collateral", "C", 18); borrowableOracle = new Oracle(); collateralOracle = new Oracle(); + irm = new IRM(); + blue.whitelistIRM(address(irm)); market = Market( - IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, lLTV + IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, address(irm), lLTV ); id = Id.wrap(keccak256(abi.encode(market))); From 2224683e4eb06ca3ebac4329526fe162b0bf7677 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 18:05:00 +0200 Subject: [PATCH 068/112] feat: rename whitelist mapping --- src/Blue.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 3b2d9922a..31bd4305a 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -50,7 +50,7 @@ contract Blue is Ownable { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; // IRM whitelist. - mapping(address => bool) public whitelistedIRMs; + mapping(address => bool) public irmWhitelist; constructor(address owner) Ownable(owner) {} @@ -58,14 +58,14 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(whitelistedIRMs[market.irm], "IRM not whitelisted"); + require(irmWhitelist[market.irm], "IRM not whitelisted"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id, market.irm); } function whitelistIRM(address irm) external onlyOwner { - whitelistedIRMs[irm] = !whitelistedIRMs[irm]; + irmWhitelist[irm] = !irmWhitelist[irm]; } // Supply management. From 9b9ed18cca94763e7fc9cf399766ee94bd9120de Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 19:27:52 +0200 Subject: [PATCH 069/112] docs: implement suggestion --- src/Blue.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Blue.sol b/src/Blue.sol index 5554ee7f5..164870bad 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -146,12 +146,14 @@ contract Blue { // Collateral management. - /// @dev The interests are not accrued since no health check is required when supplying collateral. + /// @dev Don't accrue interests because it's not required and it saves gas. function supplyCollateral(Market calldata market, uint amount) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); + // Don't accrue interests because it's not required and it saves gas. + collateral[id][msg.sender] += amount; market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); From a4d3046c793eacecaa9da1dece9035bbaafa6806 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 19:59:35 +0200 Subject: [PATCH 070/112] feat: use interface --- src/Blue.sol | 10 +++++----- src/mocks/IRMMock.sol | 4 +++- test/forge/Blue.t.sol | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 31bd4305a..0fc994e78 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -23,7 +23,7 @@ struct Market { IERC20 collateralAsset; IOracle borrowableOracle; IOracle collateralOracle; - address irm; + IIRM irm; uint lLTV; } @@ -58,14 +58,14 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(irmWhitelist[market.irm], "IRM not whitelisted"); + require(irmWhitelist[address(market.irm)], "IRM not whitelisted"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id, market.irm); } function whitelistIRM(address irm) external onlyOwner { - irmWhitelist[irm] = !irmWhitelist[irm]; + irmWhitelist[irm] = true; } // Supply management. @@ -214,13 +214,13 @@ contract Blue is Ownable { // Interests management. - function accrueInterests(Id id, address irm) private { + function accrueInterests(Id id, IIRM irm) private { uint marketTotalSupply = totalSupply[id]; if (marketTotalSupply != 0) { uint marketTotalBorrow = totalBorrow[id]; uint utilization = marketTotalBorrow.wDiv(marketTotalSupply); - uint borrowRate = IIRM(irm).rate(utilization); + uint borrowRate = irm.rate(utilization); uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol index 95839519f..2a5492869 100644 --- a/src/mocks/IRMMock.sol +++ b/src/mocks/IRMMock.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; -contract IRMMock { +import "src/interfaces/IIRM.sol"; + +contract IRMMock is IIRM { function rate(uint utilization) external pure returns (uint) { // Divide by the number of seconds in a year. // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 5c1809e78..1e683b73d 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -40,7 +40,7 @@ contract BlueTest is Test { irm = new IRM(); blue.whitelistIRM(address(irm)); market = Market( - IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, address(irm), lLTV + IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, irm, lLTV ); id = Id.wrap(keccak256(abi.encode(market))); From ac5e328fafca5c48371d2818f32adcf1c28f0e72 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 20:16:18 +0200 Subject: [PATCH 071/112] feat: more general IRM --- src/Blue.sol | 4 +--- src/interfaces/IIRM.sol | 2 +- src/mocks/IRMMock.sol | 24 ++++++++++++++++++++++-- test/forge/Blue.t.sol | 5 +++-- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 0fc994e78..754891160 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -219,9 +219,7 @@ contract Blue is Ownable { if (marketTotalSupply != 0) { uint marketTotalBorrow = totalBorrow[id]; - uint utilization = marketTotalBorrow.wDiv(marketTotalSupply); - uint borrowRate = irm.rate(utilization); - uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); + uint accruedInterests = marketTotalBorrow.wMul(irm.rate()).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; } diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIRM.sol index 1feb71fe0..91f12002d 100644 --- a/src/interfaces/IIRM.sol +++ b/src/interfaces/IIRM.sol @@ -2,5 +2,5 @@ pragma solidity >=0.5.0; interface IIRM { - function rate(uint utilization) external pure returns (uint); + function rate() external returns (uint); } diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol index 2a5492869..7c8229148 100644 --- a/src/mocks/IRMMock.sol +++ b/src/mocks/IRMMock.sol @@ -1,10 +1,30 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; -import "src/interfaces/IIRM.sol"; +import {IERC20} from "src/interfaces/IERC20.sol"; +import {IOracle} from "src/interfaces/IOracle.sol"; + +import {MathLib} from "src/libraries/MathLib.sol"; + +import "src/Blue.sol"; contract IRMMock is IIRM { - function rate(uint utilization) external pure returns (uint) { + using MathLib for uint; + + Blue public blue; + Id public marketId; + + constructor (Blue blueInstance) { + blue = Blue(blueInstance); + } + + function setId(Id id) external { + marketId = id; + } + + function rate() external view returns (uint) { + uint utilization = blue.totalBorrow(marketId).wDiv(blue.totalSupply(marketId)); + // Divide by the number of seconds in a year. // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. return utilization / 365 days; diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 1e683b73d..a098fba39 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -37,13 +37,14 @@ contract BlueTest is Test { collateralAsset = new ERC20("collateral", "C", 18); borrowableOracle = new Oracle(); collateralOracle = new Oracle(); - irm = new IRM(); - blue.whitelistIRM(address(irm)); + irm = new IRM(blue); market = Market( IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, irm, lLTV ); + blue.whitelistIRM(address(irm)); id = Id.wrap(keccak256(abi.encode(market))); + irm.setId(id); blue.createMarket(market); // We set the price of the borrowable asset to zero so that borrowers From 5bc9d6e70f9b30da016c1521e070205145ef7d40 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 20:18:17 +0200 Subject: [PATCH 072/112] refactor: naming --- src/Blue.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 754891160..51e012ad1 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -50,7 +50,7 @@ contract Blue is Ownable { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; // IRM whitelist. - mapping(address => bool) public irmWhitelist; + mapping(address => bool) public isIRMWhitelisted; constructor(address owner) Ownable(owner) {} @@ -58,14 +58,14 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(irmWhitelist[address(market.irm)], "IRM not whitelisted"); + require(isIRMWhitelisted[address(market.irm)], "IRM not whitelisted"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id, market.irm); } function whitelistIRM(address irm) external onlyOwner { - irmWhitelist[irm] = true; + isIRMWhitelisted[irm] = true; } // Supply management. From e0ce0f5ea11ab58a0c6853ba5af912e40347f6a4 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 20:26:45 +0200 Subject: [PATCH 073/112] test: add tests + remove console log --- src/Ownable.sol | 2 -- test/forge/Blue.t.sol | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Ownable.sol b/src/Ownable.sol index 0de7d2d63..f664124b4 100644 --- a/src/Ownable.sol +++ b/src/Ownable.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; -import "forge-std/console.sol"; - /// @title Ownable /// @author Morpho Labs /// @custom:contact security@morpho.xyz diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index a098fba39..b38a908b4 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -120,6 +120,20 @@ contract BlueTest is Test { blue2.transferOwnership(newOwner); } + function testWhitelistIRMWhenNotOwner(address attacker) public { + vm.assume(attacker != blue.owner()); + + vm.prank(attacker); + vm.expectRevert("not owner"); + blue.whitelistIRM(address(0xdead)); + } + + function testWhitelistIRM(address newIRM) public { + blue.whitelistIRM(newIRM); + + assertTrue(blue.isIRMWhitelisted(newIRM)); + } + function testSupply(uint amount) public { amount = bound(amount, 1, 2 ** 64); From 78c4ca11af9993f9438d685140523666b39074ea Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 5 Jul 2023 20:31:23 +0200 Subject: [PATCH 074/112] perf: save gas --- src/Blue.sol | 6 +++--- test/forge/Blue.t.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 51e012ad1..8af65aafd 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -50,7 +50,7 @@ contract Blue is Ownable { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; // IRM whitelist. - mapping(address => bool) public isIRMWhitelisted; + mapping(address => uint) public isIRMWhitelisted; constructor(address owner) Ownable(owner) {} @@ -58,14 +58,14 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(isIRMWhitelisted[address(market.irm)], "IRM not whitelisted"); + require(isIRMWhitelisted[address(market.irm)] == 1, "IRM not whitelisted"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id, market.irm); } function whitelistIRM(address irm) external onlyOwner { - isIRMWhitelisted[irm] = true; + isIRMWhitelisted[irm] = 1; } // Supply management. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index b38a908b4..ae055e0ba 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -131,7 +131,7 @@ contract BlueTest is Test { function testWhitelistIRM(address newIRM) public { blue.whitelistIRM(newIRM); - assertTrue(blue.isIRMWhitelisted(newIRM)); + assertEq(blue.isIRMWhitelisted(newIRM), 1); } function testSupply(uint amount) public { From 88ccfb94bb6d44c266e2bfcda7300aeba3c1c523 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 08:02:02 +0200 Subject: [PATCH 075/112] refactor: remove event --- src/Ownable.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Ownable.sol b/src/Ownable.sol index f664124b4..0d226345e 100644 --- a/src/Ownable.sol +++ b/src/Ownable.sol @@ -6,10 +6,6 @@ pragma solidity >=0.8.0; /// @custom:contact security@morpho.xyz /// @dev Greatly inspired by Solmate and OZ implementations. abstract contract Ownable { - /* EVENTS */ - - event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); - /* STORAGE */ address public owner; From 06d5441cdf268cebb29525e65a5efa148cb7d0ba Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 08:11:30 +0200 Subject: [PATCH 076/112] fix: removal --- src/Ownable.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Ownable.sol b/src/Ownable.sol index 0d226345e..6dfee241c 100644 --- a/src/Ownable.sol +++ b/src/Ownable.sol @@ -21,15 +21,11 @@ abstract contract Ownable { constructor(address newOwner) { owner = newOwner; - - emit OwnershipTransferred(address(0), newOwner); } /* PUBLIC */ function transferOwnership(address newOwner) public virtual onlyOwner { owner = newOwner; - - emit OwnershipTransferred(msg.sender, newOwner); } } From ad03870d98190c4ec0dab43edac974a797d42bf1 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 10:55:45 +0200 Subject: [PATCH 077/112] feat: implement suggestions --- src/Ownable.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ownable.sol b/src/Ownable.sol index 6dfee241c..ff1484e35 100644 --- a/src/Ownable.sol +++ b/src/Ownable.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.20; /// @title Ownable /// @author Morpho Labs @@ -12,7 +12,7 @@ abstract contract Ownable { /* MODIFIERS */ - modifier onlyOwner() virtual { + modifier onlyOwner() { require(msg.sender == owner, "not owner"); _; } @@ -25,7 +25,7 @@ abstract contract Ownable { /* PUBLIC */ - function transferOwnership(address newOwner) public virtual onlyOwner { + function transferOwnership(address newOwner) external onlyOwner { owner = newOwner; } } From f5111f4a769a42e762741f4a5267fcb79f96e004 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Thu, 6 Jul 2023 11:00:41 +0200 Subject: [PATCH 078/112] refactor: improve hardhat testing style --- test/hardhat/Blue.spec.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index 965dce9c5..94bd7902f 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -1,7 +1,7 @@ import { hexZeroPad } from "@ethersproject/bytes"; import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import assert from "assert"; +import { expect } from "chai"; import { BigNumber, Wallet, constants, utils } from "ethers"; import hre from "hardhat"; import { Blue, OracleMock, ERC20Mock } from "types"; @@ -9,7 +9,7 @@ import { Blue, OracleMock, ERC20Mock } from "types"; const iterations = 500; const closePositions = false; const nbLiquidations = 5; -assert(2 * nbLiquidations + 1 < 20, "more liquidations than signers"); +expect(2 * nbLiquidations + 1 < 20, "more liquidations than signers"); const initBalance = constants.MaxUint256.div(2); let seed = 42; @@ -89,7 +89,7 @@ describe("Blue", () => { await blue.connect(admin).createMarket(market); }); - it("should simulate gas cost", async () => { + it("should simulate gas cost [main]", async () => { for (let i = 1; i < iterations; ++i) { console.log(i, "/", iterations); @@ -124,26 +124,26 @@ describe("Blue", () => { } }); - it("should simulate liquidations gas cost", async () => { + it("should simulate gas cost [liquidations]", async () => { let liquidationData = []; // Create accounts close to liquidation for (let i = 0; i < 2 * nbLiquidations; ++i) { const user = signers[i]; const tranche = Math.floor(1 + i / 2); - const lltv = BigNumber.WAD.mul(tranche).div(nbLiquidations + 1); + const lLTV = BigNumber.WAD.mul(tranche).div(nbLiquidations + 1); const amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100)); - const borrowedAmount = amount.mul(lltv).div(BigNumber.WAD); + const borrowedAmount = amount.mul(lLTV).div(BigNumber.WAD); const maxSeize = closePositions ? constants.MaxUint256 : amount.div(2); - market.lLTV = lltv; + market.lLTV = lLTV; // We use 2 different users to borrow from a bucket so that liquidations do not close a bucket completely. // Consequently, we should only create the market on a particular LLTV once. if (i % 2 == 0) { await blue.connect(admin).createMarket(market); liquidationData.push({ - lltv: lltv, + lLTV: lLTV, borrower: user.address, maxSeize: maxSeize, }); @@ -168,24 +168,24 @@ describe("Blue", () => { await borrowable.setBalance(liquidator.address, initBalance); for (let i = 0; i < liquidationData.length; i++) { let data = liquidationData[i]; - market.lLTV = data.lltv; + market.lLTV = data.lLTV; await blue.connect(liquidator).liquidate(market, data.borrower, data.maxSeize); } for (let i = 0; i < 2 * nbLiquidations; i++) { const user = signers[i]; const tranche = Math.floor(1 + i / 2); - const lltv = BigNumber.WAD.mul(tranche).div(nbLiquidations + 1); + const lLTV = BigNumber.WAD.mul(tranche).div(nbLiquidations + 1); - market.lLTV = lltv; + market.lLTV = lLTV; id = identifier(market); let collat = await blue.collateral(id, user.address); - assert( + expect( !closePositions || collat == BigNumber.from(0), "did not take the whole collateral when closing the position" ); - assert(closePositions || collat != BigNumber.from(0), "unexpectedly closed the position"); + expect(closePositions || collat != BigNumber.from(0), "unexpectedly closed the position"); } }); }); From 03a129c1cab4f940be8e0d1a5a0543db4c988eb0 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Thu, 6 Jul 2023 11:03:03 +0200 Subject: [PATCH 079/112] docs: comment on closing a market in gas test --- test/hardhat/Blue.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index 94bd7902f..a1e96541c 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -138,7 +138,7 @@ describe("Blue", () => { const maxSeize = closePositions ? constants.MaxUint256 : amount.div(2); market.lLTV = lLTV; - // We use 2 different users to borrow from a bucket so that liquidations do not close a bucket completely. + // We use 2 different users to borrow from a market so that liquidations do not put the borrow storage back to 0 on that market. // Consequently, we should only create the market on a particular LLTV once. if (i % 2 == 0) { await blue.connect(admin).createMarket(market); From 5363c1da7572bb48d0e9195e04e56bed155827d4 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 11:17:44 +0200 Subject: [PATCH 080/112] refactor: apply changes --- src/Blue.sol | 2 +- src/interfaces/IIRM.sol | 4 +++- src/mocks/IRMMock.sol | 11 +++-------- test/forge/Blue.t.sol | 22 +++++++++++++--------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 8af65aafd..328abebc8 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -219,7 +219,7 @@ contract Blue is Ownable { if (marketTotalSupply != 0) { uint marketTotalBorrow = totalBorrow[id]; - uint accruedInterests = marketTotalBorrow.wMul(irm.rate()).wMul(block.timestamp - lastUpdate[id]); + uint accruedInterests = marketTotalBorrow.wMul(irm.borrowRate(id)).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; } diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIRM.sol index 91f12002d..22523a14f 100644 --- a/src/interfaces/IIRM.sol +++ b/src/interfaces/IIRM.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; +import {Id} from "src/Blue.sol"; + interface IIRM { - function rate() external returns (uint); + function borrowRate(Id id) external returns (uint); } diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol index 7c8229148..102d5a098 100644 --- a/src/mocks/IRMMock.sol +++ b/src/mocks/IRMMock.sol @@ -11,19 +11,14 @@ import "src/Blue.sol"; contract IRMMock is IIRM { using MathLib for uint; - Blue public blue; - Id public marketId; + Blue public immutable blue; constructor (Blue blueInstance) { blue = Blue(blueInstance); } - function setId(Id id) external { - marketId = id; - } - - function rate() external view returns (uint) { - uint utilization = blue.totalBorrow(marketId).wDiv(blue.totalSupply(marketId)); + function borrowRate(Id id) external view returns (uint) { + uint utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); // Divide by the number of seconds in a year. // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index ae055e0ba..1c647edfa 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -18,6 +18,7 @@ contract BlueTest is Test { address private constant borrower = address(1234); address private constant liquidator = address(5678); uint private constant lLTV = 0.8 ether; + address private constant OWNER = address(0xdead); Blue private blue; ERC20 private borrowableAsset; @@ -30,22 +31,24 @@ contract BlueTest is Test { function setUp() public { // Create Blue. - blue = new Blue(address(this)); + blue = new Blue(OWNER); // List a market. borrowableAsset = new ERC20("borrowable", "B", 18); collateralAsset = new ERC20("collateral", "C", 18); borrowableOracle = new Oracle(); collateralOracle = new Oracle(); + irm = new IRM(blue); market = Market( IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, irm, lLTV ); - blue.whitelistIRM(address(irm)); id = Id.wrap(keccak256(abi.encode(market))); - irm.setId(id); + vm.startPrank(OWNER); + blue.whitelistIRM(address(irm)); blue.createMarket(market); + vm.stopPrank(); // We set the price of the borrowable asset to zero so that borrowers // don't need to deposit any collateral. @@ -96,10 +99,10 @@ contract BlueTest is Test { // Tests - function testOwner(address owner) public { - Blue blue2 = new Blue(owner); + function testOwner(address newOwner) public { + Blue blue2 = new Blue(newOwner); - assertEq(blue2.owner(), owner, "owner"); + assertEq(blue2.owner(), newOwner, "owner"); } function testTransferOwnership(address oldOwner, address newOwner) public { @@ -111,9 +114,9 @@ contract BlueTest is Test { } function testTransferOwnershipWhenNotOwner(address attacker, address newOwner) public { - vm.assume(attacker != address(0xdead)); + vm.assume(attacker != OWNER); - Blue blue2 = new Blue(address(0xdead)); + Blue blue2 = new Blue(OWNER); vm.prank(attacker); vm.expectRevert("not owner"); @@ -125,10 +128,11 @@ contract BlueTest is Test { vm.prank(attacker); vm.expectRevert("not owner"); - blue.whitelistIRM(address(0xdead)); + blue.whitelistIRM(OWNER); } function testWhitelistIRM(address newIRM) public { + vm.prank(OWNER); blue.whitelistIRM(newIRM); assertEq(blue.isIRMWhitelisted(newIRM), 1); From 36802db789beb7fe519900f94259d2457e759c97 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 11:26:25 +0200 Subject: [PATCH 081/112] feat: move function to admin place --- src/Blue.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 328abebc8..fe82bc308 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -64,10 +64,6 @@ contract Blue is Ownable { accrueInterests(id, market.irm); } - function whitelistIRM(address irm) external onlyOwner { - isIRMWhitelisted[irm] = 1; - } - // Supply management. function supply(Market calldata market, uint amount) external { @@ -212,6 +208,12 @@ contract Blue is Ownable { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), repaid); } + // Admin functions. + + function whitelistIRM(address irm) external onlyOwner { + isIRMWhitelisted[irm] = 1; + } + // Interests management. function accrueInterests(Id id, IIRM irm) private { From 2692eb87b21b466ab0b28dbf7ca58ecfee3379cf Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 11:32:32 +0200 Subject: [PATCH 082/112] refactor: get back to bool --- src/Blue.sol | 6 +++--- test/forge/Blue.t.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index fe82bc308..8fc1249c6 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -50,7 +50,7 @@ contract Blue is Ownable { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; // IRM whitelist. - mapping(address => uint) public isIRMWhitelisted; + mapping(address => bool) public isIRMWhitelisted; constructor(address owner) Ownable(owner) {} @@ -58,7 +58,7 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(isIRMWhitelisted[address(market.irm)] == 1, "IRM not whitelisted"); + require(isIRMWhitelisted[address(market.irm)], "IRM not whitelisted"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id, market.irm); @@ -211,7 +211,7 @@ contract Blue is Ownable { // Admin functions. function whitelistIRM(address irm) external onlyOwner { - isIRMWhitelisted[irm] = 1; + isIRMWhitelisted[irm] = true; } // Interests management. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 1c647edfa..8294a2e5f 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -135,7 +135,7 @@ contract BlueTest is Test { vm.prank(OWNER); blue.whitelistIRM(newIRM); - assertEq(blue.isIRMWhitelisted(newIRM), 1); + assertTrue(blue.isIRMWhitelisted(newIRM)); } function testSupply(uint amount) public { From cc22ada24b013ff529bc95fcaa753e05698785b9 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 11:36:51 +0200 Subject: [PATCH 083/112] refactor: whitelist -> enable --- src/Blue.sol | 10 +++++----- test/forge/Blue.t.sol | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 8fc1249c6..65153c69e 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -49,8 +49,8 @@ contract Blue is Ownable { mapping(Id => uint) public totalBorrowShares; // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; - // IRM whitelist. - mapping(address => bool) public isIRMWhitelisted; + // Enabled IRMs. + mapping(address => bool) public isIrmEnabled; constructor(address owner) Ownable(owner) {} @@ -58,7 +58,7 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(isIRMWhitelisted[address(market.irm)], "IRM not whitelisted"); + require(isIrmEnabled[address(market.irm)], "IRM not enabled"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(id, market.irm); @@ -210,8 +210,8 @@ contract Blue is Ownable { // Admin functions. - function whitelistIRM(address irm) external onlyOwner { - isIRMWhitelisted[irm] = true; + function enableIrm(address irm) external onlyOwner { + isIrmEnabled[irm] = true; } // Interests management. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 8294a2e5f..ef11eb878 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -46,7 +46,7 @@ contract BlueTest is Test { id = Id.wrap(keccak256(abi.encode(market))); vm.startPrank(OWNER); - blue.whitelistIRM(address(irm)); + blue.enableIrm(address(irm)); blue.createMarket(market); vm.stopPrank(); @@ -123,19 +123,19 @@ contract BlueTest is Test { blue2.transferOwnership(newOwner); } - function testWhitelistIRMWhenNotOwner(address attacker) public { + function testEnableIrmWhenNotOwner(address attacker) public { vm.assume(attacker != blue.owner()); vm.prank(attacker); vm.expectRevert("not owner"); - blue.whitelistIRM(OWNER); + blue.enableIrm(OWNER); } - function testWhitelistIRM(address newIRM) public { + function testEnableIrm(address newIRM) public { vm.prank(OWNER); - blue.whitelistIRM(newIRM); + blue.enableIrm(newIRM); - assertTrue(blue.isIRMWhitelisted(newIRM)); + assertTrue(blue.isIrmEnabled(newIRM)); } function testSupply(uint amount) public { From ffc05c223f420fee223a1179d64aa0efb822f103 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 11:45:40 +0200 Subject: [PATCH 084/112] test: add missing test + format --- src/interfaces/IIRM.sol | 2 +- src/mocks/IRMMock.sol | 22 +++++++++++----------- test/forge/Blue.t.sol | 40 +++++++++++++++++++++++++++++++++------- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIRM.sol index 22523a14f..8960f1d27 100644 --- a/src/interfaces/IIRM.sol +++ b/src/interfaces/IIRM.sol @@ -4,5 +4,5 @@ pragma solidity >=0.5.0; import {Id} from "src/Blue.sol"; interface IIRM { - function borrowRate(Id id) external returns (uint); + function borrowRate(Id id) external returns (uint); } diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol index 102d5a098..aca182f27 100644 --- a/src/mocks/IRMMock.sol +++ b/src/mocks/IRMMock.sol @@ -9,19 +9,19 @@ import {MathLib} from "src/libraries/MathLib.sol"; import "src/Blue.sol"; contract IRMMock is IIRM { - using MathLib for uint; + using MathLib for uint; - Blue public immutable blue; + Blue public immutable blue; - constructor (Blue blueInstance) { - blue = Blue(blueInstance); - } + constructor(Blue blueInstance) { + blue = Blue(blueInstance); + } - function borrowRate(Id id) external view returns (uint) { - uint utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); + function borrowRate(Id id) external view returns (uint) { + uint utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); - // Divide by the number of seconds in a year. - // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. - return utilization / 365 days; - } + // Divide by the number of seconds in a year. + // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. + return utilization / 365 days; + } } diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index ef11eb878..c9ad68643 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -41,7 +41,12 @@ contract BlueTest is Test { irm = new IRM(blue); market = Market( - IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), borrowableOracle, collateralOracle, irm, lLTV + IERC20(address(borrowableAsset)), + IERC20(address(collateralAsset)), + borrowableOracle, + collateralOracle, + irm, + lLTV ); id = Id.wrap(keccak256(abi.encode(market))); @@ -131,11 +136,26 @@ contract BlueTest is Test { blue.enableIrm(OWNER); } - function testEnableIrm(address newIRM) public { + function testEnableIrm(address newIrm) public { vm.prank(OWNER); - blue.enableIrm(newIRM); + blue.enableIrm(newIrm); - assertTrue(blue.isIrmEnabled(newIRM)); + assertTrue(blue.isIrmEnabled(newIrm)); + } + + function testCreateMarketNotEnabledIrm( + IERC20 newBorrowableAsset, + IERC20 newCollateralAsset, + IOracle newBorrowableOracle, + IOracle newCollateralOracle, + IIRM newIrm, + uint newlLTV + ) public { + market = Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, newlLTV); + + vm.prank(OWNER); + vm.expectRevert("IRM not enabled"); + blue.createMarket(market); } function testSupply(uint amount) public { @@ -200,9 +220,13 @@ contract BlueTest is Test { blue.withdraw(market, amountWithdrawn); - assertApproxEqAbs(blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3, "supply share"); + assertApproxEqAbs( + blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3, "supply share" + ); assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn, "this balance"); - assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn, "blue balance"); + assertEq( + borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed - amountWithdrawn, "blue balance" + ); } function testCollateralRequirements( @@ -252,7 +276,9 @@ contract BlueTest is Test { blue.repay(market, amountRepaid); vm.stopPrank(); - assertApproxEqAbs(blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share"); + assertApproxEqAbs( + blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share" + ); assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid, "borrower balance"); assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } From 4be86576ffdaa079138b7216c92a16331d4649b4 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 11:52:25 +0200 Subject: [PATCH 085/112] chore: format pre-commit --- .husky/commit-msg | 4 + .husky/post-checkout | 5 + .husky/post-merge | 5 + .husky/pre-commit | 4 + package.json | 7 + yarn.lock | 799 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 795 insertions(+), 29 deletions(-) create mode 100755 .husky/commit-msg create mode 100755 .husky/post-checkout create mode 100755 .husky/post-merge create mode 100755 .husky/pre-commit diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 000000000..313a5f2cb --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +yarn commitlint --edit "${1}" diff --git a/.husky/post-checkout b/.husky/post-checkout new file mode 100755 index 000000000..a531ca6f5 --- /dev/null +++ b/.husky/post-checkout @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +git submodule update --init --recursive +yarn diff --git a/.husky/post-merge b/.husky/post-merge new file mode 100755 index 000000000..a531ca6f5 --- /dev/null +++ b/.husky/post-merge @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +git submodule update --init --recursive +yarn diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..d2ae35e84 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged diff --git a/package.json b/package.json index 4381200e5..8758c28f9 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "openzeppelin-contracts": "^4.0.0" }, "devDependencies": { + "@commitlint/cli": "^17.6.6", + "@commitlint/config-conventional": "^17.6.6", "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", "@nomicfoundation/hardhat-foundry": "^1.0.1", "@nomicfoundation/hardhat-network-helpers": "^1.0.8", @@ -45,5 +47,10 @@ "*.ts": "yarn prettier", "*.json": "yarn prettier", "*.yml": "yarn prettier" + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] } } diff --git a/yarn.lock b/yarn.lock index 9fe12863f..f271cdb1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.22.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== @@ -157,6 +157,167 @@ "@chainsafe/persistent-merkle-tree" "^0.4.2" case "^1.6.3" +"@commitlint/cli@^17.6.6": + version "17.6.6" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.6.6.tgz#416da9c45901323e5bf931aa1eac5995a3aa251c" + integrity sha512-sTKpr2i/Fjs9OmhU+beBxjPavpnLSqZaO6CzwKVq2Tc4UYVTMFgpKOslDhUBVlfAUBfjVO8ParxC/MXkIOevEA== + dependencies: + "@commitlint/format" "^17.4.4" + "@commitlint/lint" "^17.6.6" + "@commitlint/load" "^17.5.0" + "@commitlint/read" "^17.5.1" + "@commitlint/types" "^17.4.4" + execa "^5.0.0" + lodash.isfunction "^3.0.9" + resolve-from "5.0.0" + resolve-global "1.0.0" + yargs "^17.0.0" + +"@commitlint/config-conventional@^17.6.6": + version "17.6.6" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.6.6.tgz#5452aa601d34503b88530ba38432116bcffdd005" + integrity sha512-phqPz3BDhfj49FUYuuZIuDiw+7T6gNAEy7Yew1IBHqSohVUCWOK2FXMSAExzS2/9X+ET93g0Uz83KjiHDOOFag== + dependencies: + conventional-changelog-conventionalcommits "^5.0.0" + +"@commitlint/config-validator@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.4.4.tgz#d0742705719559a101d2ee49c0c514044af6d64d" + integrity sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg== + dependencies: + "@commitlint/types" "^17.4.4" + ajv "^8.11.0" + +"@commitlint/ensure@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.4.4.tgz#a36e7719bdb9c2b86c8b8c2e852b463a7bfda5fa" + integrity sha512-AHsFCNh8hbhJiuZ2qHv/m59W/GRE9UeOXbkOqxYMNNg9pJ7qELnFcwj5oYpa6vzTSHtPGKf3C2yUFNy1GGHq6g== + dependencies: + "@commitlint/types" "^17.4.4" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.upperfirst "^4.3.1" + +"@commitlint/execute-rule@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz#4518e77958893d0a5835babe65bf87e2638f6939" + integrity sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA== + +"@commitlint/format@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-17.4.4.tgz#0f6e1b4d7a301c7b1dfd4b6334edd97fc050b9f5" + integrity sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ== + dependencies: + "@commitlint/types" "^17.4.4" + chalk "^4.1.0" + +"@commitlint/is-ignored@^17.6.6": + version "17.6.6" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.6.6.tgz#b1c869757bdea659aa582669ea0066798ed6a17e" + integrity sha512-4Fw875faAKO+2nILC04yW/2Vy/wlV3BOYCSQ4CEFzriPEprc1Td2LILmqmft6PDEK5Sr14dT9tEzeaZj0V56Gg== + dependencies: + "@commitlint/types" "^17.4.4" + semver "7.5.2" + +"@commitlint/lint@^17.6.6": + version "17.6.6" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.6.6.tgz#d7ff64b6783f2bda56526195a66e6bb587e1fe1a" + integrity sha512-5bN+dnHcRLkTvwCHYMS7Xpbr+9uNi0Kq5NR3v4+oPNx6pYXt8ACuw9luhM/yMgHYwW0ajIR20wkPAFkZLEMGmg== + dependencies: + "@commitlint/is-ignored" "^17.6.6" + "@commitlint/parse" "^17.6.5" + "@commitlint/rules" "^17.6.5" + "@commitlint/types" "^17.4.4" + +"@commitlint/load@^17.5.0": + version "17.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.5.0.tgz#be45dbbb50aaf5eb7e8e940e1e0d6171d1426bab" + integrity sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q== + dependencies: + "@commitlint/config-validator" "^17.4.4" + "@commitlint/execute-rule" "^17.4.0" + "@commitlint/resolve-extends" "^17.4.4" + "@commitlint/types" "^17.4.4" + "@types/node" "*" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + cosmiconfig-typescript-loader "^4.0.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + resolve-from "^5.0.0" + ts-node "^10.8.1" + typescript "^4.6.4 || ^5.0.0" + +"@commitlint/message@^17.4.2": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-17.4.2.tgz#f4753a79701ad6db6db21f69076e34de6580e22c" + integrity sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q== + +"@commitlint/parse@^17.6.5": + version "17.6.5" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.6.5.tgz#7b84b328a6a94ca08ab7c98c491d9d3dab68f09d" + integrity sha512-0zle3bcn1Hevw5Jqpz/FzEWNo2KIzUbc1XyGg6WrWEoa6GH3A1pbqNF6MvE6rjuy6OY23c8stWnb4ETRZyN+Yw== + dependencies: + "@commitlint/types" "^17.4.4" + conventional-changelog-angular "^5.0.11" + conventional-commits-parser "^3.2.2" + +"@commitlint/read@^17.5.1": + version "17.5.1" + resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-17.5.1.tgz#fec903b766e2c41e3cefa80630040fcaba4f786c" + integrity sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg== + dependencies: + "@commitlint/top-level" "^17.4.0" + "@commitlint/types" "^17.4.4" + fs-extra "^11.0.0" + git-raw-commits "^2.0.11" + minimist "^1.2.6" + +"@commitlint/resolve-extends@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz#8f931467dea8c43b9fe38373e303f7c220de6fdc" + integrity sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A== + dependencies: + "@commitlint/config-validator" "^17.4.4" + "@commitlint/types" "^17.4.4" + import-fresh "^3.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + resolve-global "^1.0.0" + +"@commitlint/rules@^17.6.5": + version "17.6.5" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.6.5.tgz#fabcacdde923e26ac5ef90d4b3f8fc05526bbaa1" + integrity sha512-uTB3zSmnPyW2qQQH+Dbq2rekjlWRtyrjDo4aLFe63uteandgkI+cc0NhhbBAzcXShzVk0qqp8SlkQMu0mgHg/A== + dependencies: + "@commitlint/ensure" "^17.4.4" + "@commitlint/message" "^17.4.2" + "@commitlint/to-lines" "^17.4.0" + "@commitlint/types" "^17.4.4" + execa "^5.0.0" + +"@commitlint/to-lines@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-17.4.0.tgz#9bd02e911e7d4eab3fb4a50376c4c6d331e10d8d" + integrity sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg== + +"@commitlint/top-level@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-17.4.0.tgz#540cac8290044cf846fbdd99f5cc51e8ac5f27d6" + integrity sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g== + dependencies: + find-up "^5.0.0" + +"@commitlint/types@^17.4.4": + version "17.4.4" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-17.4.4.tgz#1416df936e9aad0d6a7bbc979ecc31e55dade662" + integrity sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ== + dependencies: + chalk "^4.1.0" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1035,6 +1196,11 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== +"@types/minimist@^1.2.0": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== + "@types/mocha@^10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" @@ -1055,6 +1221,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + "@types/pbkdf2@^3.0.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" @@ -1087,6 +1258,14 @@ dependencies: "@types/node" "*" +JSONStream@^1.0.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1167,6 +1346,16 @@ ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.11.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -1281,6 +1470,11 @@ array-buffer-byte-length@^1.0.0: call-bind "^1.0.2" is-array-buffer "^3.0.1" +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -1302,6 +1496,11 @@ array.prototype.reduce@^1.0.5: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -1526,7 +1725,21 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -camelcase@^5.0.0: +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -1713,6 +1926,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1789,6 +2011,14 @@ commander@^10.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1804,6 +2034,35 @@ concat-stream@^1.6.0, concat-stream@^1.6.2: readable-stream "^2.2.2" typedarray "^0.0.6" +conventional-changelog-angular@^5.0.11: + version "5.0.13" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" + integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== + dependencies: + compare-func "^2.0.0" + q "^1.5.1" + +conventional-changelog-conventionalcommits@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz#41bdce54eb65a848a4a3ffdca93e92fa22b64a86" + integrity sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw== + dependencies: + compare-func "^2.0.0" + lodash "^4.17.15" + q "^1.5.1" + +conventional-commits-parser@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz#a7d3b77758a202a9b2293d2112a8d8052c740972" + integrity sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q== + dependencies: + JSONStream "^1.0.4" + is-text-path "^1.0.1" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + cookie@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" @@ -1819,6 +2078,21 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cosmiconfig-typescript-loader@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" + integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== + +cosmiconfig@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + crc-32@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" @@ -1866,6 +2140,11 @@ cross-spawn@^7.0.3: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1892,7 +2171,15 @@ debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, de dependencies: ms "2.1.2" -decamelize@^1.2.0: +decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== @@ -1974,6 +2261,13 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dot-prop@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + dotenv@^16.1.3: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" @@ -2032,6 +2326,13 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" @@ -2320,6 +2621,21 @@ evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" @@ -2404,7 +2720,7 @@ find-up@3.0.0, find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -2419,6 +2735,14 @@ find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + flat@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" @@ -2487,6 +2811,15 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^11.0.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -2585,7 +2918,7 @@ get-port@^3.1.0: resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== -get-stream@^6.0.1: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -2613,6 +2946,17 @@ ghost-testrpc@^0.0.2: chalk "^2.4.2" node-emoji "^1.10.0" +git-raw-commits@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" + integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -2679,6 +3023,13 @@ glob@^7.0.0, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg== + dependencies: + ini "^1.3.4" + global-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -2763,6 +3114,11 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + hardhat-deal@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/hardhat-deal/-/hardhat-deal-1.4.0.tgz#3f42f52603c1f9eeb7e583ca7261478d17b875a4" @@ -2936,6 +3292,18 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + http-basic@^8.1.1: version "8.1.3" resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" @@ -2981,6 +3349,11 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + human-signals@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" @@ -3013,6 +3386,14 @@ immutable@^4.0.0-rc.12: resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -3031,7 +3412,7 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.5: +ini@^1.3.4, ini@^1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -3066,6 +3447,11 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: get-intrinsic "^1.2.0" is-typed-array "^1.1.10" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -3098,7 +3484,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0: +is-core-module@^2.11.0, is-core-module@^2.5.0: version "2.12.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== @@ -3161,6 +3547,16 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -3181,6 +3577,11 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" @@ -3200,6 +3601,13 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== + dependencies: + text-extensions "^1.0.0" + is-typed-array@^1.1.10, is-typed-array@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" @@ -3289,7 +3697,7 @@ js-yaml@3.x: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@4.1.0: +js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== @@ -3306,11 +3714,21 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" @@ -3344,6 +3762,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + jsonschema@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" @@ -3368,7 +3791,7 @@ keccak@^3.0.0, keccak@^3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -kind-of@^6.0.2: +kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -3414,6 +3837,11 @@ lilconfig@2.1.0: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + lint-staged@13.2.2: version "13.2.2" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.2.tgz#5e711d3139c234f73402177be2f8dd312e6508ca" @@ -3463,6 +3891,13 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -3475,6 +3910,51 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -3536,6 +4016,16 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + markdown-table@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" @@ -3569,6 +4059,23 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== +meow@^8.0.0: + version "8.1.2" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -3609,6 +4116,11 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -3640,6 +4152,15 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -3825,11 +4346,38 @@ nopt@3.x: dependencies: abbrev "1" +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + npm-run-path@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" @@ -3908,7 +4456,7 @@ once@1.x, once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -3956,7 +4504,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -3984,6 +4532,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -4008,11 +4563,28 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-cache-control@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -4028,7 +4600,7 @@ 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== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -4116,6 +4688,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +q@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== + qs@^6.4.0, qs@^6.7.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" @@ -4133,6 +4710,11 @@ queue-microtask@^1.2.2, queue-microtask@^1.2.3: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -4150,6 +4732,34 @@ raw-body@^2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@^2.2.2: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" @@ -4163,15 +4773,6 @@ readable-stream@^2.2.2: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - readdirp@~3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" @@ -4200,6 +4801,14 @@ recursive-readdir@^2.2.2: dependencies: minimatch "^3.0.5" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + reduce-flatten@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" @@ -4275,7 +4884,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.0: +require-from-string@^2.0.0, require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -4285,11 +4894,28 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +resolve-from@5.0.0, resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-global@1.0.0, resolve-global@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-global/-/resolve-global-1.0.0.tgz#a2a79df4af2ca3f49bf77ef9ddacd322dad19255" + integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== + dependencies: + global-dirs "^0.1.1" + resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -4302,7 +4928,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@^1.1.6: +resolve@^1.1.6, resolve@^1.10.0: version "1.22.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== @@ -4450,11 +5076,18 @@ secp256k1@^4.0.1: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" -semver@^5.5.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" + semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -4540,7 +5173,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -4642,6 +5275,39 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== + +split2@^3.0.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -4711,7 +5377,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4798,6 +5464,11 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-final-newline@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" @@ -4810,6 +5481,13 @@ strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed "1.0.0" +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -4886,6 +5564,11 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + then-request@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" @@ -4903,7 +5586,14 @@ then-request@^6.0.0: promise "^8.0.0" qs "^6.4.0" -through@^2.3.8: +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +"through@>=2.2.7 <3", through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -4940,6 +5630,11 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + 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" @@ -4955,7 +5650,7 @@ ts-essentials@^7.0.1: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== -ts-node@^10.9.1: +ts-node@^10.8.1, ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== @@ -5023,16 +5718,31 @@ type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + type-fest@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + typechain@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.2.0.tgz#bd4fc8f111d4405e36858bae6f744604617b60f3" @@ -5068,6 +5778,11 @@ typescript@5.0.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== +"typescript@^4.6.4 || ^5.0.0": + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" @@ -5152,6 +5867,14 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -5331,11 +6054,16 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@^20.2.2: +yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-unparser@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" @@ -5384,6 +6112,19 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.0.0: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From bca68b97b99c71ff6d6577a4299c1648070b5429 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 12:03:13 +0200 Subject: [PATCH 086/112] chore: use forge install --- .husky/post-merge | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/post-merge b/.husky/post-merge index a531ca6f5..00af39400 100755 --- a/.husky/post-merge +++ b/.husky/post-merge @@ -1,5 +1,5 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -git submodule update --init --recursive +forge install yarn From 7718133fe3a307cadc5f04daf0dd566bd929cc42 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 14:41:46 +0200 Subject: [PATCH 087/112] chore: missing forge install --- .husky/post-checkout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/post-checkout b/.husky/post-checkout index a531ca6f5..00af39400 100755 --- a/.husky/post-checkout +++ b/.husky/post-checkout @@ -1,5 +1,5 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -git submodule update --init --recursive +forge install yarn From de937e73e35fa6ac2d243c0c2eac86c87c5a3278 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 14:57:13 +0200 Subject: [PATCH 088/112] feat: use market instead of id --- src/Blue.sol | 21 +++++++++++---------- src/interfaces/IIRM.sol | 4 ++-- src/mocks/IRMMock.sol | 3 ++- test/forge/Blue.t.sol | 3 ++- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 65153c69e..2643f0af9 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -61,7 +61,7 @@ contract Blue is Ownable { require(isIrmEnabled[address(market.irm)], "IRM not enabled"); require(lastUpdate[id] == 0, "market already exists"); - accrueInterests(id, market.irm); + accrueInterests(market, id); } // Supply management. @@ -71,7 +71,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); if (totalSupply[id] == 0) { supplyShare[id][msg.sender] = WAD; @@ -92,7 +92,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); supplyShare[id][msg.sender] -= shares; @@ -112,7 +112,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); if (totalBorrow[id] == 0) { borrowShare[id][msg.sender] = WAD; @@ -136,7 +136,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); uint shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); borrowShare[id][msg.sender] -= shares; @@ -154,7 +154,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); collateral[id][msg.sender] += amount; @@ -166,7 +166,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(amount > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); collateral[id][msg.sender] -= amount; @@ -182,7 +182,7 @@ contract Blue is Ownable { require(lastUpdate[id] != 0, "unknown market"); require(seized > 0, "zero amount"); - accrueInterests(id, market.irm); + accrueInterests(market, id); require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); @@ -216,12 +216,13 @@ contract Blue is Ownable { // Interests management. - function accrueInterests(Id id, IIRM irm) private { + function accrueInterests(Market calldata market, Id id) private { uint marketTotalSupply = totalSupply[id]; if (marketTotalSupply != 0) { uint marketTotalBorrow = totalBorrow[id]; - uint accruedInterests = marketTotalBorrow.wMul(irm.borrowRate(id)).wMul(block.timestamp - lastUpdate[id]); + uint accruedInterests = + marketTotalBorrow.wMul(market.irm.borrowRate(market)).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; } diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIRM.sol index 8960f1d27..579dc071b 100644 --- a/src/interfaces/IIRM.sol +++ b/src/interfaces/IIRM.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; -import {Id} from "src/Blue.sol"; +import {Market} from "src/Blue.sol"; interface IIRM { - function borrowRate(Id id) external returns (uint); + function borrowRate(Market calldata market) external returns (uint); } diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol index aca182f27..7a1f01907 100644 --- a/src/mocks/IRMMock.sol +++ b/src/mocks/IRMMock.sol @@ -17,7 +17,8 @@ contract IRMMock is IIRM { blue = Blue(blueInstance); } - function borrowRate(Id id) external view returns (uint) { + function borrowRate(Market calldata market) external view returns (uint) { + Id id = Id.wrap(keccak256(abi.encode(market))); uint utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); // Divide by the number of seconds in a year. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index c9ad68643..0ada5b6ca 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -151,7 +151,8 @@ contract BlueTest is Test { IIRM newIrm, uint newlLTV ) public { - market = Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, newlLTV); + market = + Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, newlLTV); vm.prank(OWNER); vm.expectRevert("IRM not enabled"); From eced257ecc83ed45baf6170e1ec234657494f6ea Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 15:51:21 +0200 Subject: [PATCH 089/112] feat: enable lLTV --- src/Blue.sol | 8 ++++++++ test/forge/Blue.t.sol | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 2643f0af9..7d4fe82df 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -51,6 +51,8 @@ contract Blue is Ownable { mapping(Id => uint) public lastUpdate; // Enabled IRMs. mapping(address => bool) public isIrmEnabled; + // Enabled lLTVs. + mapping(uint => bool) public islLTVEnabled; constructor(address owner) Ownable(owner) {} @@ -59,6 +61,7 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(isIrmEnabled[address(market.irm)], "IRM not enabled"); + require(islLTVEnabled[market.lLTV], "lLTV not enabled"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(market, id); @@ -214,6 +217,11 @@ contract Blue is Ownable { isIrmEnabled[irm] = true; } + function enablelLTV(uint lLTV) external onlyOwner { + require(lLTV < WAD, "lLTV too high"); + islLTVEnabled[lLTV] = true; + } + // Interests management. function accrueInterests(Market calldata market, Id id) private { diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 0ada5b6ca..8a945afd7 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -52,6 +52,7 @@ contract BlueTest is Test { vm.startPrank(OWNER); blue.enableIrm(address(irm)); + blue.enablelLTV(lLTV); blue.createMarket(market); vm.stopPrank(); @@ -148,17 +149,48 @@ contract BlueTest is Test { IERC20 newCollateralAsset, IOracle newBorrowableOracle, IOracle newCollateralOracle, - IIRM newIrm, - uint newlLTV + IIRM newIrm ) public { market = - Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, newlLTV); + Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, lLTV); vm.prank(OWNER); vm.expectRevert("IRM not enabled"); blue.createMarket(market); } + function testEnablelLTVWhenNotOwner(address attacker) public { + vm.assume(attacker != blue.owner()); + + vm.prank(attacker); + vm.expectRevert("not owner"); + blue.enableIrm(OWNER); + } + + function testEnablelLTV(uint newlLTV) public { + vm.assume(newlLTV < WAD); + + vm.prank(OWNER); + blue.enablelLTV(newlLTV); + + assertTrue(blue.islLTVEnabled(newlLTV)); + } + + function testCreateMarketNotEnabledlLTV( + IERC20 newBorrowableAsset, + IERC20 newCollateralAsset, + IOracle newBorrowableOracle, + IOracle newCollateralOracle, + uint newlLTV + ) public { + market = + Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, irm, newlLTV); + + vm.prank(OWNER); + vm.expectRevert("lLTV not enabled"); + blue.createMarket(market); + } + function testSupply(uint amount) public { amount = bound(amount, 1, 2 ** 64); From 5d383f3574b4f5717376ba7d59e88967ba6472c6 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 15:58:52 +0200 Subject: [PATCH 090/112] test: add missing test --- test/forge/Blue.t.sol | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 8a945afd7..70fd161b2 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -168,7 +168,7 @@ contract BlueTest is Test { } function testEnablelLTV(uint newlLTV) public { - vm.assume(newlLTV < WAD); + newlLTV = bound(newlLTV, 0, WAD - 1); vm.prank(OWNER); blue.enablelLTV(newlLTV); @@ -176,6 +176,14 @@ contract BlueTest is Test { assertTrue(blue.islLTVEnabled(newlLTV)); } + function testEnablelLTVShouldFailWhenlLTVTooHigh(uint newlLTV) public { + newlLTV = bound(newlLTV, WAD, type(uint256).max); + + vm.prank(OWNER); + vm.expectRevert("lLTV too high"); + blue.enablelLTV(newlLTV); + } + function testCreateMarketNotEnabledlLTV( IERC20 newBorrowableAsset, IERC20 newCollateralAsset, From d36f6a019603e6b1ecdc227f3614d008d8289641 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 16:19:44 +0200 Subject: [PATCH 091/112] feat: internalize owner --- src/Blue.sol | 25 +++++++++++++++++++++---- src/Ownable.sol | 31 ------------------------------- 2 files changed, 21 insertions(+), 35 deletions(-) delete mode 100644 src/Ownable.sol diff --git a/src/Blue.sol b/src/Blue.sol index 834fe13b2..3be894499 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -7,8 +7,6 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; -import {Ownable} from "src/Ownable.sol"; - uint constant WAD = 1e18; uint constant alpha = 0.5e18; @@ -31,12 +29,14 @@ function irm(uint utilization) pure returns (uint) { return utilization / 365 days; } -contract Blue is Ownable { +contract Blue { using MathLib for uint; using SafeTransferLib for IERC20; // Storage. + // Owner. + address public owner; // User' supply balances. mapping(Id => mapping(address => uint)) public supplyShare; // User' borrow balances. @@ -54,7 +54,24 @@ contract Blue is Ownable { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; - constructor(address owner) Ownable(owner) {} + // Constructor. + + constructor(address newOwner) { + owner = newOwner; + } + + // Modifiers. + + modifier onlyOwner() { + require(msg.sender == owner, "not owner"); + _; + } + + // Only owner functions. + + function transferOwnership(address newOwner) external onlyOwner { + owner = newOwner; + } // Markets management. diff --git a/src/Ownable.sol b/src/Ownable.sol deleted file mode 100644 index ff1484e35..000000000 --- a/src/Ownable.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.20; - -/// @title Ownable -/// @author Morpho Labs -/// @custom:contact security@morpho.xyz -/// @dev Greatly inspired by Solmate and OZ implementations. -abstract contract Ownable { - /* STORAGE */ - - address public owner; - - /* MODIFIERS */ - - modifier onlyOwner() { - require(msg.sender == owner, "not owner"); - _; - } - - /* CONSTRUCTOR */ - - constructor(address newOwner) { - owner = newOwner; - } - - /* PUBLIC */ - - function transferOwnership(address newOwner) external onlyOwner { - owner = newOwner; - } -} From 93f0f1c832b88b356db3f4b6d37aac495e01c606 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 18:57:16 +0200 Subject: [PATCH 092/112] refactor: renaming irm --- src/Blue.sol | 4 ++-- src/Ownable.sol | 31 +++++++++++++++++++++++++++++++ src/interfaces/IIRM.sol | 2 +- src/mocks/IRMMock.sol | 2 +- test/forge/Blue.t.sol | 8 ++++---- 5 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 src/Ownable.sol diff --git a/src/Blue.sol b/src/Blue.sol index c35aeaf0b..9f8d405fc 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; -import {IIRM} from "src/interfaces/IIRM.sol"; +import {IIrm} from "src/interfaces/IIrm.sol"; import {IERC20} from "src/interfaces/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; @@ -21,7 +21,7 @@ struct Market { IERC20 collateralAsset; IOracle borrowableOracle; IOracle collateralOracle; - IIRM irm; + IIrm irm; uint lLTV; } diff --git a/src/Ownable.sol b/src/Ownable.sol new file mode 100644 index 000000000..ff1484e35 --- /dev/null +++ b/src/Ownable.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.20; + +/// @title Ownable +/// @author Morpho Labs +/// @custom:contact security@morpho.xyz +/// @dev Greatly inspired by Solmate and OZ implementations. +abstract contract Ownable { + /* STORAGE */ + + address public owner; + + /* MODIFIERS */ + + modifier onlyOwner() { + require(msg.sender == owner, "not owner"); + _; + } + + /* CONSTRUCTOR */ + + constructor(address newOwner) { + owner = newOwner; + } + + /* PUBLIC */ + + function transferOwnership(address newOwner) external onlyOwner { + owner = newOwner; + } +} diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIRM.sol index 579dc071b..035dd2e82 100644 --- a/src/interfaces/IIRM.sol +++ b/src/interfaces/IIRM.sol @@ -3,6 +3,6 @@ pragma solidity >=0.5.0; import {Market} from "src/Blue.sol"; -interface IIRM { +interface IIrm { function borrowRate(Market calldata market) external returns (uint); } diff --git a/src/mocks/IRMMock.sol b/src/mocks/IRMMock.sol index 7a1f01907..46d53b6dd 100644 --- a/src/mocks/IRMMock.sol +++ b/src/mocks/IRMMock.sol @@ -8,7 +8,7 @@ import {MathLib} from "src/libraries/MathLib.sol"; import "src/Blue.sol"; -contract IRMMock is IIRM { +contract IrmMock is IIrm { using MathLib for uint; Blue public immutable blue; diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 0ada5b6ca..44a006ea0 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -10,7 +10,7 @@ import "forge-std/console.sol"; import "src/Blue.sol"; import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; -import {IRMMock as IRM} from "src/mocks/IRMMock.sol"; +import {IrmMock as Irm} from "src/mocks/IrmMock.sol"; contract BlueTest is Test { using MathLib for uint; @@ -25,7 +25,7 @@ contract BlueTest is Test { ERC20 private collateralAsset; Oracle private borrowableOracle; Oracle private collateralOracle; - IRM private irm; + Irm private irm; Market public market; Id public id; @@ -39,7 +39,7 @@ contract BlueTest is Test { borrowableOracle = new Oracle(); collateralOracle = new Oracle(); - irm = new IRM(blue); + irm = new Irm(blue); market = Market( IERC20(address(borrowableAsset)), IERC20(address(collateralAsset)), @@ -148,7 +148,7 @@ contract BlueTest is Test { IERC20 newCollateralAsset, IOracle newBorrowableOracle, IOracle newCollateralOracle, - IIRM newIrm, + IIrm newIrm, uint newlLTV ) public { market = From d52f15aedd433543f52922a1fb70c23b1151c157 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Thu, 6 Jul 2023 18:42:46 +0200 Subject: [PATCH 093/112] chore: increase number of fuzz/invariant on CI --- .github/workflows/test.yml | 13 +------------ foundry.toml | 10 ---------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 236d3cd2a..a4544f79c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,8 +3,6 @@ name: Foundry CI on: workflow_dispatch: push: - branches: - - main pull_request: jobs: @@ -20,16 +18,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Run Forge build - run: | - forge --version - forge build --sizes - id: build - name: Run Forge tests - run: | - forge test -vvv + run: FOUNDRY_FUZZ_RUNS=100000 FOUNDRY_FUZZ_MAX_TEST_REJECTS=500000 FOUNDRY_INVARIANT_RUNS=1000 FOUNDRY_INVARIANT_DEPTH=100 forge test -vvv id: test diff --git a/foundry.toml b/foundry.toml index c332bfbfa..9fec6e2f8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,16 +1,6 @@ [profile.default] -src = "src" -out = "out" -libs = ["lib"] via-ir = true -[fuzz] -runs = 2048 - -[invariant] -runs = 2048 -depth = 32 - [fmt] int_types = "short" From 6dd7fcaee2f0f307914a0a8257c473362e4ec526 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 19:17:36 +0200 Subject: [PATCH 094/112] test: better tests + add one --- test/forge/Blue.t.sol | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 44a006ea0..c7f95acc9 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -143,20 +143,19 @@ contract BlueTest is Test { assertTrue(blue.isIrmEnabled(newIrm)); } - function testCreateMarketNotEnabledIrm( - IERC20 newBorrowableAsset, - IERC20 newCollateralAsset, - IOracle newBorrowableOracle, - IOracle newCollateralOracle, - IIrm newIrm, - uint newlLTV - ) public { - market = - Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, newlLTV); + function testCreateMarketWithEnabledIrm(Market memory marketFuzz) public { + vm.startPrank(OWNER); + blue.enableIrm(address(marketFuzz.irm)); + blue.createMarket(marketFuzz); + vm.stopPrank(); + } + + function testCreateMarketWithNotEnabledIrm(Market memory marketFuzz) public { + vm.assume(marketFuzz.irm != irm); vm.prank(OWNER); vm.expectRevert("IRM not enabled"); - blue.createMarket(market); + blue.createMarket(marketFuzz); } function testSupply(uint amount) public { @@ -420,7 +419,7 @@ contract BlueTest is Test { } function testUnknownMarket(Market memory marketFuzz) public { - vm.assume(neq(marketFuzz, market)); + vm.assume(notSameMarkets(marketFuzz, market)); vm.expectRevert("unknown market"); blue.supply(marketFuzz, 1); @@ -481,7 +480,8 @@ contract BlueTest is Test { } } -function neq(Market memory a, Market memory b) pure returns (bool) { +function notSameMarkets(Market memory a, Market memory b) pure returns (bool) { return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset - || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV; + || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV + || a.irm != b.irm; } From fe71f20a8dbba82e80653764e4277003b40fb7e6 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 19:26:27 +0200 Subject: [PATCH 095/112] feat: cleaner code --- src/Blue.sol | 6 +++--- test/forge/Blue.t.sol | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 9f8d405fc..3d521ca49 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -50,7 +50,7 @@ contract Blue { // Interests last update (used to check if a market has been created). mapping(Id => uint) public lastUpdate; // Enabled IRMs. - mapping(address => bool) public isIrmEnabled; + mapping(IIrm => bool) public isIrmEnabled; // Constructor. @@ -71,7 +71,7 @@ contract Blue { owner = newOwner; } - function enableIrm(address irm) external onlyOwner { + function enableIrm(IIrm irm) external onlyOwner { isIrmEnabled[irm] = true; } @@ -79,7 +79,7 @@ contract Blue { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); - require(isIrmEnabled[address(market.irm)], "IRM not enabled"); + require(isIrmEnabled[market.irm], "IRM not enabled"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(market, id); diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index c7f95acc9..c55f915ae 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -51,7 +51,7 @@ contract BlueTest is Test { id = Id.wrap(keccak256(abi.encode(market))); vm.startPrank(OWNER); - blue.enableIrm(address(irm)); + blue.enableIrm(irm); blue.createMarket(market); vm.stopPrank(); @@ -128,15 +128,15 @@ contract BlueTest is Test { blue2.transferOwnership(newOwner); } - function testEnableIrmWhenNotOwner(address attacker) public { + function testEnableIrmWhenNotOwner(address attacker, IIrm newIrm) public { vm.assume(attacker != blue.owner()); vm.prank(attacker); vm.expectRevert("not owner"); - blue.enableIrm(OWNER); + blue.enableIrm(newIrm); } - function testEnableIrm(address newIrm) public { + function testEnableIrm(IIrm newIrm) public { vm.prank(OWNER); blue.enableIrm(newIrm); @@ -145,7 +145,7 @@ contract BlueTest is Test { function testCreateMarketWithEnabledIrm(Market memory marketFuzz) public { vm.startPrank(OWNER); - blue.enableIrm(address(marketFuzz.irm)); + blue.enableIrm(marketFuzz.irm); blue.createMarket(marketFuzz); vm.stopPrank(); } @@ -419,7 +419,7 @@ contract BlueTest is Test { } function testUnknownMarket(Market memory marketFuzz) public { - vm.assume(notSameMarkets(marketFuzz, market)); + vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); blue.supply(marketFuzz, 1); @@ -480,7 +480,7 @@ contract BlueTest is Test { } } -function notSameMarkets(Market memory a, Market memory b) pure returns (bool) { +function neq(Market memory a, Market memory b) pure returns (bool) { return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV || a.irm != b.irm; From 2baa13e04194510915a02480f1f5159661fddc21 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 21:21:30 +0200 Subject: [PATCH 096/112] refactor: renaming --- src/Blue.sol | 16 +++++++-------- test/forge/Blue.t.sol | 46 +++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 7d4fe82df..a4554bcac 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -24,7 +24,7 @@ struct Market { IOracle borrowableOracle; IOracle collateralOracle; IIRM irm; - uint lLTV; + uint lltv; } contract Blue is Ownable { @@ -52,7 +52,7 @@ contract Blue is Ownable { // Enabled IRMs. mapping(address => bool) public isIrmEnabled; // Enabled lLTVs. - mapping(uint => bool) public islLTVEnabled; + mapping(uint => bool) public isLltvEnabled; constructor(address owner) Ownable(owner) {} @@ -61,7 +61,7 @@ contract Blue is Ownable { function createMarket(Market calldata market) external { Id id = Id.wrap(keccak256(abi.encode(market))); require(isIrmEnabled[address(market.irm)], "IRM not enabled"); - require(islLTVEnabled[market.lLTV], "lLTV not enabled"); + require(isLltvEnabled[market.lltv], "lltv not enabled"); require(lastUpdate[id] == 0, "market already exists"); accrueInterests(market, id); @@ -190,7 +190,7 @@ contract Blue is Ownable { require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); // The liquidation incentive is 1 + alpha * (1 / LLTV - 1). - uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); + uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lltv) - WAD); uint repaid = seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price()); uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); @@ -217,9 +217,9 @@ contract Blue is Ownable { isIrmEnabled[irm] = true; } - function enablelLTV(uint lLTV) external onlyOwner { - require(lLTV < WAD, "lLTV too high"); - islLTVEnabled[lLTV] = true; + function enableLltv(uint lltv) external onlyOwner { + require(lltv < WAD, "LLTV too high"); + isLltvEnabled[lltv] = true; } // Interests management. @@ -247,6 +247,6 @@ contract Blue is Ownable { ? borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price()) : 0; uint collateralValue = collateral[id][user].wMul(market.collateralOracle.price()); - return collateralValue.wMul(market.lLTV) >= borrowValue; + return collateralValue.wMul(market.lltv) >= borrowValue; } } diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 70fd161b2..22bfbc16e 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -17,7 +17,7 @@ contract BlueTest is Test { address private constant borrower = address(1234); address private constant liquidator = address(5678); - uint private constant lLTV = 0.8 ether; + uint private constant lltv = 0.8 ether; address private constant OWNER = address(0xdead); Blue private blue; @@ -46,13 +46,13 @@ contract BlueTest is Test { borrowableOracle, collateralOracle, irm, - lLTV + lltv ); id = Id.wrap(keccak256(abi.encode(market))); vm.startPrank(OWNER); blue.enableIrm(address(irm)); - blue.enablelLTV(lLTV); + blue.enableLltv(lltv); blue.createMarket(market); vm.stopPrank(); @@ -152,7 +152,7 @@ contract BlueTest is Test { IIRM newIrm ) public { market = - Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, lLTV); + Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, newIrm, lltv); vm.prank(OWNER); vm.expectRevert("IRM not enabled"); @@ -167,35 +167,35 @@ contract BlueTest is Test { blue.enableIrm(OWNER); } - function testEnablelLTV(uint newlLTV) public { - newlLTV = bound(newlLTV, 0, WAD - 1); + function testEnableLltv(uint newLltv) public { + newLltv = bound(newLltv, 0, WAD - 1); vm.prank(OWNER); - blue.enablelLTV(newlLTV); + blue.enableLltv(newLltv); - assertTrue(blue.islLTVEnabled(newlLTV)); + assertTrue(blue.isLltvEnabled(newLltv)); } - function testEnablelLTVShouldFailWhenlLTVTooHigh(uint newlLTV) public { - newlLTV = bound(newlLTV, WAD, type(uint256).max); + function testEnableLltvShouldFailWhenlLtvTooHigh(uint newLltv) public { + newLltv = bound(newLltv, WAD, type(uint256).max); vm.prank(OWNER); - vm.expectRevert("lLTV too high"); - blue.enablelLTV(newlLTV); + vm.expectRevert("LLTV too high"); + blue.enableLltv(newLltv); } - function testCreateMarketNotEnabledlLTV( + function testCreateMarketNotEnabledLltv( IERC20 newBorrowableAsset, IERC20 newCollateralAsset, IOracle newBorrowableOracle, IOracle newCollateralOracle, - uint newlLTV + uint newLltv ) public { market = - Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, irm, newlLTV); + Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, irm, newLltv); vm.prank(OWNER); - vm.expectRevert("lLTV not enabled"); + vm.expectRevert("lltv not enabled"); blue.createMarket(market); } @@ -294,7 +294,7 @@ contract BlueTest is Test { uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); - if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(lLTV))) { + if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(lltv))) { vm.prank(borrower); blue.borrow(market, amountBorrowed); } else { @@ -360,10 +360,10 @@ contract BlueTest is Test { amountLent = bound(amountLent, 1000, 2 ** 64); uint amountCollateral = amountLent; - uint borrowingPower = amountCollateral.wMul(lLTV); + uint borrowingPower = amountCollateral.wMul(lltv); uint amountBorrowed = borrowingPower.wMul(0.8e18); - uint toSeize = amountCollateral.wMul(lLTV); - uint incentive = WAD + alpha.wMul(WAD.wDiv(lLTV) - WAD); + uint toSeize = amountCollateral.wMul(lltv); + uint incentive = WAD + alpha.wMul(WAD.wDiv(lltv) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(borrower, amountCollateral); @@ -402,10 +402,10 @@ contract BlueTest is Test { amountLent = bound(amountLent, 1000, 2 ** 64); uint amountCollateral = amountLent; - uint borrowingPower = amountCollateral.wMul(lLTV); + uint borrowingPower = amountCollateral.wMul(lltv); uint amountBorrowed = borrowingPower.wMul(0.8e18); uint toSeize = amountCollateral; - uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lLTV) - WAD); + uint incentive = WAD + alpha.wMul(WAD.wDiv(market.lltv) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(borrower, amountCollateral); @@ -523,5 +523,5 @@ contract BlueTest is Test { function neq(Market memory a, Market memory b) pure returns (bool) { return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset - || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV; + || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lltv != b.lltv; } From 4738d931f207a68a299e97431b69aa001a981878 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 21:41:20 +0200 Subject: [PATCH 097/112] test: improve tests --- test/forge/Blue.t.sol | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index d336fb269..265a720dd 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -103,6 +103,10 @@ contract BlueTest is Test { assertLe(blue.totalBorrow(id), blue.totalSupply(id), "liquidity"); } + function invariantLltvEnabled() public { + assertTrue(blue.isLltvEnabled(lltv)); + } + // Tests function testOwner(address newOwner) public { @@ -162,7 +166,7 @@ contract BlueTest is Test { } function testEnableLltvWhenNotOwner(address attacker, uint newLltv) public { - vm.assume(attacker != blue.owner()); + vm.assume(attacker != OWNER); vm.prank(attacker); vm.expectRevert("not owner"); @@ -178,7 +182,7 @@ contract BlueTest is Test { assertTrue(blue.isLltvEnabled(newLltv)); } - function testEnableLltvShouldFailWhenlLtvTooHigh(uint newLltv) public { + function testEnableLltvShouldFailWhenLltvTooHigh(uint newLltv) public { newLltv = bound(newLltv, WAD, type(uint).max); vm.prank(OWNER); @@ -186,18 +190,13 @@ contract BlueTest is Test { blue.enableLltv(newLltv); } - function testCreateMarketNotEnabledLltv( - IERC20 newBorrowableAsset, - IERC20 newCollateralAsset, - IOracle newBorrowableOracle, - IOracle newCollateralOracle, - uint newLltv - ) public { - market = Market(newBorrowableAsset, newCollateralAsset, newBorrowableOracle, newCollateralOracle, irm, newLltv); + function testCreateMarketWithNotEnabledLltv(Market memory marketFuzz) public { + vm.assume(marketFuzz.lltv != lltv); + marketFuzz.irm = irm; vm.prank(OWNER); vm.expectRevert("LLTV not enabled"); - blue.createMarket(market); + blue.createMarket(marketFuzz); } function testSupply(uint amount) public { From c916251c8623a9f37246fda424fc6fe4fd54db6a Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 6 Jul 2023 22:15:49 +0200 Subject: [PATCH 098/112] fix: issue with naming --- src/interfaces/{IIRM.sol => IIrm.sol} | 0 src/mocks/{IRMMock.sol => IrmMock.sol} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/interfaces/{IIRM.sol => IIrm.sol} (100%) rename src/mocks/{IRMMock.sol => IrmMock.sol} (100%) diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIrm.sol similarity index 100% rename from src/interfaces/IIRM.sol rename to src/interfaces/IIrm.sol diff --git a/src/mocks/IRMMock.sol b/src/mocks/IrmMock.sol similarity index 100% rename from src/mocks/IRMMock.sol rename to src/mocks/IrmMock.sol From d87e02d23bb836926309bc5f74d48f2faf1a0297 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 7 Jul 2023 10:43:22 +0200 Subject: [PATCH 099/112] chore: refactor CI yml --- .github/workflows/test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a4544f79c..a5a06ea09 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,5 +20,9 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 - name: Run Forge tests - run: FOUNDRY_FUZZ_RUNS=100000 FOUNDRY_FUZZ_MAX_TEST_REJECTS=500000 FOUNDRY_INVARIANT_RUNS=1000 FOUNDRY_INVARIANT_DEPTH=100 forge test -vvv - id: test + run: forge test -vvv + env: + FOUNDRY_FUZZ_RUNS: 100000 + FOUNDRY_FUZZ_MAX_TEST_REJECTS: 500000 + FOUNDRY_INVARIANT_RUNS: 1000 + FOUNDRY_INVARIANT_DEPTH: 100 From 4f9fb84348d9d838651daaecadab3eda9030eaf8 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 7 Jul 2023 13:55:30 +0200 Subject: [PATCH 100/112] fix: issue with naming --- src/interfaces/{IIRM.sol => IIrm.sol} | 0 src/mocks/{IRMMock.sol => IrmMock.sol} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/interfaces/{IIRM.sol => IIrm.sol} (100%) rename src/mocks/{IRMMock.sol => IrmMock.sol} (100%) diff --git a/src/interfaces/IIRM.sol b/src/interfaces/IIrm.sol similarity index 100% rename from src/interfaces/IIRM.sol rename to src/interfaces/IIrm.sol diff --git a/src/mocks/IRMMock.sol b/src/mocks/IrmMock.sol similarity index 100% rename from src/mocks/IRMMock.sol rename to src/mocks/IrmMock.sol From d6963be4cdbf57b81f236c83f1cd3f8034433f35 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 7 Jul 2023 13:56:19 +0200 Subject: [PATCH 101/112] feat: remove useless ownable --- src/Ownable.sol | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/Ownable.sol diff --git a/src/Ownable.sol b/src/Ownable.sol deleted file mode 100644 index ff1484e35..000000000 --- a/src/Ownable.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.20; - -/// @title Ownable -/// @author Morpho Labs -/// @custom:contact security@morpho.xyz -/// @dev Greatly inspired by Solmate and OZ implementations. -abstract contract Ownable { - /* STORAGE */ - - address public owner; - - /* MODIFIERS */ - - modifier onlyOwner() { - require(msg.sender == owner, "not owner"); - _; - } - - /* CONSTRUCTOR */ - - constructor(address newOwner) { - owner = newOwner; - } - - /* PUBLIC */ - - function transferOwnership(address newOwner) external onlyOwner { - owner = newOwner; - } -} From 32337d64dec26d7a7e1ddbb8b03973056d68d2fa Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 7 Jul 2023 14:19:44 +0200 Subject: [PATCH 102/112] refactor: recreate borrowRate var --- src/Blue.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 64446f309..ac617df2e 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -242,8 +242,8 @@ contract Blue { if (marketTotalSupply != 0) { uint marketTotalBorrow = totalBorrow[id]; - uint accruedInterests = - marketTotalBorrow.wMul(market.irm.borrowRate(market)).wMul(block.timestamp - lastUpdate[id]); + uint borrowRate = market.irm.borrowRate(market); + uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; } From 79a07d7dd1bd950e42b2a9d0133272e3872cd948 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 7 Jul 2023 14:20:18 +0200 Subject: [PATCH 103/112] test: fix hardat tests --- test/hardhat/Blue.spec.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index bbbf95af3..d10b0a43e 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -3,7 +3,7 @@ import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { BigNumber, Wallet, constants, utils } from "ethers"; import hre from "hardhat"; -import { Blue, OracleMock, ERC20Mock } from "types"; +import { Blue, OracleMock, ERC20Mock, IrmMock } from "types"; const iterations = 500; @@ -23,6 +23,7 @@ interface Market { collateralAsset: string; borrowableOracle: string; collateralOracle: string; + irm: string; lLTV: BigNumber; } @@ -34,6 +35,7 @@ describe("Blue", () => { let collateral: ERC20Mock; let borrowableOracle: OracleMock; let collateralOracle: OracleMock; + let irm: IrmMock; let market: Market; let id: Buffer; @@ -54,22 +56,28 @@ describe("Blue", () => { const BlueFactory = await hre.ethers.getContractFactory("Blue", signers[0]); - blue = await BlueFactory.deploy(); + blue = await BlueFactory.deploy(signers[0].address); + + const IrmMockFactory = await hre.ethers.getContractFactory("IrmMock", signers[0]); + + irm = await IrmMockFactory.deploy(blue.address); market = { borrowableAsset: borrowable.address, collateralAsset: collateral.address, borrowableOracle: borrowableOracle.address, collateralOracle: collateralOracle.address, + irm: irm.address, lLTV: BigNumber.WAD, }; const abiCoder = new utils.AbiCoder(); const values = Object.values(market); - const encodedMarket = abiCoder.encode(["address", "address", "address", "address", "uint256"], values); + const encodedMarket = abiCoder.encode(["address", "address", "address", "address", "address", "uint256"], values); id = Buffer.from(utils.keccak256(encodedMarket).slice(2), "hex"); + await blue.connect(signers[0]).enableIrm(irm.address); await blue.connect(signers[0]).createMarket(market); }); From d6f6f2a953925cf8452118f6757006ced21c5f77 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Fri, 7 Jul 2023 15:51:18 +0200 Subject: [PATCH 104/112] fix: using expect correctly --- hardhat.config.ts | 3 +++ test/hardhat/Blue.spec.ts | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 7a452f5e9..244c2c163 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -20,6 +20,9 @@ const config: HardhatUserConfig = { chainId: 1, gasPrice: 0, initialBaseFeePerGas: 0, + accounts: { + count: 102, + }, }, }, solidity: { diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index a1e96541c..41b95a963 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -8,8 +8,8 @@ import { Blue, OracleMock, ERC20Mock } from "types"; const iterations = 500; const closePositions = false; -const nbLiquidations = 5; -expect(2 * nbLiquidations + 1 < 20, "more liquidations than signers"); +const nbLiquidations = 50; +// The liquidations gas test expects that 2*nbLiquidations + 1 is strictly less than the number of signers. const initBalance = constants.MaxUint256.div(2); let seed = 42; @@ -184,8 +184,8 @@ describe("Blue", () => { expect( !closePositions || collat == BigNumber.from(0), "did not take the whole collateral when closing the position" - ); - expect(closePositions || collat != BigNumber.from(0), "unexpectedly closed the position"); + ).true; + expect(closePositions || collat != BigNumber.from(0), "unexpectedly closed the position").true; } }); }); From 2c89aa9c4862f1799dbb8bb558939a70f49a5706 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 7 Jul 2023 19:19:47 +0200 Subject: [PATCH 105/112] test: capital letters for constants --- test/forge/Blue.t.sol | 98 +++++++++++++++++++-------------------- test/hardhat/Blue.spec.ts | 4 +- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index cf2090ccb..d18ce5838 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -15,9 +15,9 @@ import {IrmMock as Irm} from "src/mocks/IrmMock.sol"; contract BlueTest is Test { using MathLib for uint; - address private constant borrower = address(1234); - address private constant liquidator = address(5678); - uint private constant lLTV = 0.8 ether; + address private constant BORROWER = address(1234); + address private constant LIQUIDATOR = address(5678); + uint private constant LLTV = 0.8 ether; address private constant OWNER = address(0xdead); Blue private blue; @@ -46,7 +46,7 @@ contract BlueTest is Test { borrowableOracle, collateralOracle, irm, - lLTV + LLTV ); id = Id.wrap(keccak256(abi.encode(market))); @@ -62,11 +62,11 @@ contract BlueTest is Test { borrowableAsset.approve(address(blue), type(uint).max); collateralAsset.approve(address(blue), type(uint).max); - vm.startPrank(borrower); + vm.startPrank(BORROWER); borrowableAsset.approve(address(blue), type(uint).max); collateralAsset.approve(address(blue), type(uint).max); vm.stopPrank(); - vm.startPrank(liquidator); + vm.startPrank(LIQUIDATOR); borrowableAsset.approve(address(blue), type(uint).max); collateralAsset.approve(address(blue), type(uint).max); vm.stopPrank(); @@ -182,17 +182,17 @@ contract BlueTest is Test { } if (amountBorrowed > amountLent) { - vm.prank(borrower); + vm.prank(BORROWER); vm.expectRevert("not enough liquidity"); blue.borrow(market, amountBorrowed); return; } - vm.prank(borrower); + vm.prank(BORROWER); blue.borrow(market, amountBorrowed); - assertEq(blue.borrowShare(id, borrower), 1e18, "borrow share"); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed, "borrower balance"); + assertEq(blue.borrowShare(id, BORROWER), 1e18, "borrow share"); + assertEq(borrowableAsset.balanceOf(BORROWER), amountBorrowed, "BORROWER balance"); assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed, "blue balance"); } @@ -205,7 +205,7 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), amountLent); blue.supply(market, amountLent); - vm.prank(borrower); + vm.prank(BORROWER); blue.borrow(market, amountBorrowed); if (amountWithdrawn > amountLent - amountBorrowed) { @@ -244,20 +244,20 @@ contract BlueTest is Test { collateralOracle.setPrice(priceCollateral); borrowableAsset.setBalance(address(this), amountBorrowed); - collateralAsset.setBalance(borrower, amountCollateral); + collateralAsset.setBalance(BORROWER, amountCollateral); blue.supply(market, amountBorrowed); - vm.prank(borrower); + vm.prank(BORROWER); blue.supplyCollateral(market, amountCollateral); uint collateralValue = amountCollateral.wMul(priceCollateral); uint borrowValue = amountBorrowed.wMul(priceBorrowable); - if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(lLTV))) { - vm.prank(borrower); + if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(LLTV))) { + vm.prank(BORROWER); blue.borrow(market, amountBorrowed); } else { - vm.prank(borrower); + vm.prank(BORROWER); vm.expectRevert("not enough collateral"); blue.borrow(market, amountBorrowed); } @@ -271,15 +271,15 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), amountLent); blue.supply(market, amountLent); - vm.startPrank(borrower); + vm.startPrank(BORROWER); blue.borrow(market, amountBorrowed); blue.repay(market, amountRepaid); vm.stopPrank(); assertApproxEqAbs( - blue.borrowShare(id, borrower), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share" + blue.borrowShare(id, BORROWER), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share" ); - assertEq(borrowableAsset.balanceOf(borrower), amountBorrowed - amountRepaid, "borrower balance"); + assertEq(borrowableAsset.balanceOf(BORROWER), amountBorrowed - amountRepaid, "BORROWER balance"); assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } @@ -319,20 +319,20 @@ contract BlueTest is Test { amountLent = bound(amountLent, 1000, 2 ** 64); uint amountCollateral = amountLent; - uint borrowingPower = amountCollateral.wMul(lLTV); + uint borrowingPower = amountCollateral.wMul(LLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); - uint toSeize = amountCollateral.wMul(lLTV); - uint incentive = WAD + ALPHA.wMul(WAD.wDiv(lLTV) - WAD); + uint toSeize = amountCollateral.wMul(LLTV); + uint incentive = WAD + ALPHA.wMul(WAD.wDiv(LLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); - collateralAsset.setBalance(borrower, amountCollateral); - borrowableAsset.setBalance(liquidator, amountBorrowed); + collateralAsset.setBalance(BORROWER, amountCollateral); + borrowableAsset.setBalance(LIQUIDATOR, amountBorrowed); // Supply blue.supply(market, amountLent); // Borrow - vm.startPrank(borrower); + vm.startPrank(BORROWER); blue.supplyCollateral(market, amountCollateral); blue.borrow(market, amountBorrowed); vm.stopPrank(); @@ -340,20 +340,20 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(2e18); - uint liquidatorNetWorthBefore = netWorth(liquidator); + uint liquidatorNetWorthBefore = netWorth(LIQUIDATOR); // Liquidate - vm.prank(liquidator); - blue.liquidate(market, borrower, toSeize); + vm.prank(LIQUIDATOR); + blue.liquidate(market, BORROWER, toSeize); - uint liquidatorNetWorthAfter = netWorth(liquidator); + uint liquidatorNetWorthAfter = netWorth(LIQUIDATOR); uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); uint expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) - expectedRepaid.wMul(borrowableOracle.price()); - assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "liquidator net worth"); - assertApproxEqAbs(borrowBalance(borrower), amountBorrowed - expectedRepaid, 100, "borrower balance"); - assertEq(blue.collateral(id, borrower), amountCollateral - toSeize, "borrower collateral"); + assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth"); + assertApproxEqAbs(borrowBalance(BORROWER), amountBorrowed - expectedRepaid, 100, "BORROWER balance"); + assertEq(blue.collateral(id, BORROWER), amountCollateral - toSeize, "BORROWER collateral"); } function testRealizeBadDebt(uint amountLent) public { @@ -361,20 +361,20 @@ contract BlueTest is Test { amountLent = bound(amountLent, 1000, 2 ** 64); uint amountCollateral = amountLent; - uint borrowingPower = amountCollateral.wMul(lLTV); + uint borrowingPower = amountCollateral.wMul(LLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); uint toSeize = amountCollateral; - uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lLTV) - WAD); + uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.LLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); - collateralAsset.setBalance(borrower, amountCollateral); - borrowableAsset.setBalance(liquidator, amountBorrowed); + collateralAsset.setBalance(BORROWER, amountCollateral); + borrowableAsset.setBalance(LIQUIDATOR, amountBorrowed); // Supply blue.supply(market, amountLent); // Borrow - vm.startPrank(borrower); + vm.startPrank(BORROWER); blue.supplyCollateral(market, amountCollateral); blue.borrow(market, amountBorrowed); vm.stopPrank(); @@ -382,20 +382,20 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(100e18); - uint liquidatorNetWorthBefore = netWorth(liquidator); + uint liquidatorNetWorthBefore = netWorth(LIQUIDATOR); // Liquidate - vm.prank(liquidator); - blue.liquidate(market, borrower, toSeize); + vm.prank(LIQUIDATOR); + blue.liquidate(market, BORROWER, toSeize); - uint liquidatorNetWorthAfter = netWorth(liquidator); + uint liquidatorNetWorthAfter = netWorth(LIQUIDATOR); uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); uint expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) - expectedRepaid.wMul(borrowableOracle.price()); - assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "liquidator net worth"); - assertEq(borrowBalance(borrower), 0, "borrower balance"); - assertEq(blue.collateral(id, borrower), 0, "borrower collateral"); + assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth"); + assertEq(borrowBalance(BORROWER), 0, "BORROWER balance"); + assertEq(blue.collateral(id, BORROWER), 0, "BORROWER collateral"); uint expectedBadDebt = amountBorrowed - expectedRepaid; assertGt(expectedBadDebt, 0, "bad debt"); assertApproxEqAbs(supplyBalance(address(this)), amountLent - expectedBadDebt, 10, "lender supply balance"); @@ -408,14 +408,14 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), firstAmount); blue.supply(market, firstAmount); - borrowableAsset.setBalance(borrower, secondAmount); - vm.prank(borrower); + borrowableAsset.setBalance(BORROWER, secondAmount); + vm.prank(BORROWER); blue.supply(market, secondAmount); assertApproxEqAbs(supplyBalance(address(this)), firstAmount, 100, "same balance first user"); assertEq(blue.supplyShare(id, address(this)), 1e18, "expected shares first user"); - assertApproxEqAbs(supplyBalance(borrower), secondAmount, 100, "same balance second user"); - assertEq(blue.supplyShare(id, borrower), secondAmount * 1e18 / firstAmount, "expected shares second user"); + assertApproxEqAbs(supplyBalance(BORROWER), secondAmount, 100, "same balance second user"); + assertEq(blue.supplyShare(id, BORROWER), secondAmount * 1e18 / firstAmount, "expected shares second user"); } function testUnknownMarket(Market memory marketFuzz) public { @@ -482,6 +482,6 @@ contract BlueTest is Test { function neq(Market memory a, Market memory b) pure returns (bool) { return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset - || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV + || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.LLTV != b.LLTV || a.irm != b.irm; } diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index d10b0a43e..d91ff3544 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -24,7 +24,7 @@ interface Market { borrowableOracle: string; collateralOracle: string; irm: string; - lLTV: BigNumber; + LLTV: BigNumber; } describe("Blue", () => { @@ -68,7 +68,7 @@ describe("Blue", () => { borrowableOracle: borrowableOracle.address, collateralOracle: collateralOracle.address, irm: irm.address, - lLTV: BigNumber.WAD, + LLTV: BigNumber.WAD, }; const abiCoder = new utils.AbiCoder(); From 057e0d7b74e7a7e338c26c3834050ed1f3c79a23 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 7 Jul 2023 19:29:51 +0200 Subject: [PATCH 106/112] test: fix test --- test/forge/Blue.t.sol | 4 ++-- test/hardhat/Blue.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index d18ce5838..bf0f9d8d8 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -364,7 +364,7 @@ contract BlueTest is Test { uint borrowingPower = amountCollateral.wMul(LLTV); uint amountBorrowed = borrowingPower.wMul(0.8e18); uint toSeize = amountCollateral; - uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.LLTV) - WAD); + uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(BORROWER, amountCollateral); @@ -482,6 +482,6 @@ contract BlueTest is Test { function neq(Market memory a, Market memory b) pure returns (bool) { return a.borrowableAsset != b.borrowableAsset || a.collateralAsset != b.collateralAsset - || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.LLTV != b.LLTV + || a.borrowableOracle != b.borrowableOracle || a.collateralOracle != b.collateralOracle || a.lLTV != b.lLTV || a.irm != b.irm; } diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index d91ff3544..d10b0a43e 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -24,7 +24,7 @@ interface Market { borrowableOracle: string; collateralOracle: string; irm: string; - LLTV: BigNumber; + lLTV: BigNumber; } describe("Blue", () => { @@ -68,7 +68,7 @@ describe("Blue", () => { borrowableOracle: borrowableOracle.address, collateralOracle: collateralOracle.address, irm: irm.address, - LLTV: BigNumber.WAD, + lLTV: BigNumber.WAD, }; const abiCoder = new utils.AbiCoder(); From 6c1d8425bb4fb96ca45da015160ceba5e6dd5591 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Sat, 8 Jul 2023 09:57:51 +0200 Subject: [PATCH 107/112] refactor: remove useless imports --- src/mocks/IrmMock.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mocks/IrmMock.sol b/src/mocks/IrmMock.sol index 46d53b6dd..ebaf35386 100644 --- a/src/mocks/IrmMock.sol +++ b/src/mocks/IrmMock.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; -import {IERC20} from "src/interfaces/IERC20.sol"; -import {IOracle} from "src/interfaces/IOracle.sol"; - import {MathLib} from "src/libraries/MathLib.sol"; import "src/Blue.sol"; From e1336df10fcc7b019349f6d643ed3fb6e2cb967e Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Sat, 8 Jul 2023 21:35:56 +0200 Subject: [PATCH 108/112] fix: hardhat test with lltv --- test/hardhat/Blue.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index 43d19389d..455a8ec48 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -87,12 +87,13 @@ describe("Blue", () => { borrowableOracle: borrowableOracle.address, collateralOracle: collateralOracle.address, irm: irm.address, - lltv: BigNumber.WAD, + lltv: BigNumber.WAD.div(2), }; id = identifier(market); - await blue.connect(admin).enableIrm(irm.address); + await blue.connect(admin).enableLltv(market.lltv); + await blue.connect(admin).enableIrm(market.irm); await blue.connect(admin).createMarket(market); }); @@ -148,6 +149,8 @@ describe("Blue", () => { // We use 2 different users to borrow from a market so that liquidations do not put the borrow storage back to 0 on that market. // Consequently, we should only create the market on a particular lltv once. if (i % 2 == 0) { + await blue.connect(admin).enableLltv(market.lltv); + await blue.connect(admin).enableIrm(market.irm); await blue.connect(admin).createMarket(market); liquidationData.push({ lltv: lltv, From 99cfa52e4e75551a230a1b74c0cdb7b510592b6c Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Mon, 10 Jul 2023 10:57:44 +0200 Subject: [PATCH 109/112] refactor: check borrow instead of supply when accruing interests --- src/Blue.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index ac617df2e..d32b8353c 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -238,10 +238,10 @@ contract Blue { // Interests management. function accrueInterests(Market calldata market, Id id) private { - uint marketTotalSupply = totalSupply[id]; + uint marketTotalBorrow = totalBorrow[id]; - if (marketTotalSupply != 0) { - uint marketTotalBorrow = totalBorrow[id]; + if (marketTotalBorrow != 0) { + uint marketTotalSupply = totalSupply[id]; uint borrowRate = market.irm.borrowRate(market); uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; From a51d0de8f44f976ea7b95028ad6d755def1b4ad9 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Mon, 10 Jul 2023 11:28:36 +0200 Subject: [PATCH 110/112] docs: comment about total supply non-zero --- src/Blue.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blue.sol b/src/Blue.sol index d32b8353c..d138a8d64 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -241,6 +241,7 @@ contract Blue { uint marketTotalBorrow = totalBorrow[id]; if (marketTotalBorrow != 0) { + // The total supply is non-zero when the total borrow is non-zero. uint marketTotalSupply = totalSupply[id]; uint borrowRate = market.irm.borrowRate(market); uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); From d8726535430f9081e712045e3eeaa92c38a00fdf Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Mon, 10 Jul 2023 11:50:18 +0200 Subject: [PATCH 111/112] refactor: uint -> uint256 --- foundry.toml | 3 - src/Blue.sol | 71 +++++++++--------- src/interfaces/IIrm.sol | 2 +- src/interfaces/IOracle.sol | 2 +- src/libraries/MathLib.sol | 8 +-- src/libraries/SafeTransferLib.sol | 4 +- src/mocks/ERC20Mock.sol | 2 +- src/mocks/IrmMock.sol | 6 +- src/mocks/OracleMock.sol | 4 +- test/forge/Blue.t.sol | 116 +++++++++++++++--------------- 10 files changed, 108 insertions(+), 110 deletions(-) diff --git a/foundry.toml b/foundry.toml index 9fec6e2f8..cdb428d89 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,7 +1,4 @@ [profile.default] via-ir = true -[fmt] -int_types = "short" - # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/src/Blue.sol b/src/Blue.sol index dc940ebbe..906f59ccb 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -8,8 +8,8 @@ import {IOracle} from "src/interfaces/IOracle.sol"; import {MathLib} from "src/libraries/MathLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; -uint constant WAD = 1e18; -uint constant ALPHA = 0.5e18; +uint256 constant WAD = 1e18; +uint256 constant ALPHA = 0.5e18; // Market id. type Id is bytes32; @@ -21,7 +21,7 @@ struct Market { IOracle borrowableOracle; IOracle collateralOracle; IIrm irm; - uint lltv; + uint256 lltv; } using {toId} for Market; @@ -31,7 +31,7 @@ function toId(Market calldata market) pure returns (Id) { } contract Blue { - using MathLib for uint; + using MathLib for uint256; using SafeTransferLib for IERC20; // Storage. @@ -39,25 +39,25 @@ contract Blue { // Owner. address public owner; // User' supply balances. - mapping(Id => mapping(address => uint)) public supplyShare; + mapping(Id => mapping(address => uint256)) public supplyShare; // User' borrow balances. - mapping(Id => mapping(address => uint)) public borrowShare; + mapping(Id => mapping(address => uint256)) public borrowShare; // User' collateral balance. - mapping(Id => mapping(address => uint)) public collateral; + mapping(Id => mapping(address => uint256)) public collateral; // Market total supply. - mapping(Id => uint) public totalSupply; + mapping(Id => uint256) public totalSupply; // Market total supply shares. - mapping(Id => uint) public totalSupplyShares; + mapping(Id => uint256) public totalSupplyShares; // Market total borrow. - mapping(Id => uint) public totalBorrow; + mapping(Id => uint256) public totalBorrow; // Market total borrow shares. - mapping(Id => uint) public totalBorrowShares; + mapping(Id => uint256) public totalBorrowShares; // Interests last update (used to check if a market has been created). - mapping(Id => uint) public lastUpdate; + mapping(Id => uint256) public lastUpdate; // Enabled IRMs. mapping(IIrm => bool) public isIrmEnabled; // Enabled LLTVs. - mapping(uint => bool) public isLltvEnabled; + mapping(uint256 => bool) public isLltvEnabled; // Constructor. @@ -82,7 +82,7 @@ contract Blue { isIrmEnabled[irm] = true; } - function enableLltv(uint lltv) external onlyOwner { + function enableLltv(uint256 lltv) external onlyOwner { require(lltv < WAD, "LLTV too high"); isLltvEnabled[lltv] = true; } @@ -100,7 +100,7 @@ contract Blue { // Supply management. - function supply(Market calldata market, uint amount) external { + function supply(Market calldata market, uint256 amount) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -111,7 +111,7 @@ contract Blue { supplyShare[id][msg.sender] = WAD; totalSupplyShares[id] = WAD; } else { - uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); + uint256 shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); supplyShare[id][msg.sender] += shares; totalSupplyShares[id] += shares; } @@ -121,14 +121,14 @@ contract Blue { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); } - function withdraw(Market calldata market, uint amount) external { + function withdraw(Market calldata market, uint256 amount) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); accrueInterests(market, id); - uint shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); + uint256 shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); supplyShare[id][msg.sender] -= shares; totalSupplyShares[id] -= shares; @@ -141,7 +141,7 @@ contract Blue { // Borrow management. - function borrow(Market calldata market, uint amount) external { + function borrow(Market calldata market, uint256 amount) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -152,7 +152,7 @@ contract Blue { borrowShare[id][msg.sender] = WAD; totalBorrowShares[id] = WAD; } else { - uint shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + uint256 shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); borrowShare[id][msg.sender] += shares; totalBorrowShares[id] += shares; } @@ -165,14 +165,14 @@ contract Blue { market.borrowableAsset.safeTransfer(msg.sender, amount); } - function repay(Market calldata market, uint amount) external { + function repay(Market calldata market, uint256 amount) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); accrueInterests(market, id); - uint shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + uint256 shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); borrowShare[id][msg.sender] -= shares; totalBorrowShares[id] -= shares; @@ -184,7 +184,7 @@ contract Blue { // Collateral management. /// @dev Don't accrue interests because it's not required and it saves gas. - function supplyCollateral(Market calldata market, uint amount) external { + function supplyCollateral(Market calldata market, uint256 amount) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -196,7 +196,7 @@ contract Blue { market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); } - function withdrawCollateral(Market calldata market, uint amount) external { + function withdrawCollateral(Market calldata market, uint256 amount) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -212,7 +212,7 @@ contract Blue { // Liquidation. - function liquidate(Market calldata market, address borrower, uint seized) external { + function liquidate(Market calldata market, address borrower, uint256 seized) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(seized != 0, "zero amount"); @@ -222,9 +222,10 @@ contract Blue { require(!isHealthy(market, id, borrower), "cannot liquidate a healthy position"); // The liquidation incentive is 1 + ALPHA * (1 / LLTV - 1). - uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lltv) - WAD); - uint repaid = seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price()); - uint repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); + uint256 incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lltv) - WAD); + uint256 repaid = + seized.wMul(market.collateralOracle.price()).wDiv(incentive).wDiv(market.borrowableOracle.price()); + uint256 repaidShares = repaid.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); borrowShare[id][borrower] -= repaidShares; totalBorrowShares[id] -= repaidShares; @@ -246,12 +247,12 @@ contract Blue { // Interests management. function accrueInterests(Market calldata market, Id id) private { - uint marketTotalSupply = totalSupply[id]; + uint256 marketTotalSupply = totalSupply[id]; if (marketTotalSupply != 0) { - uint marketTotalBorrow = totalBorrow[id]; - uint borrowRate = market.irm.borrowRate(market); - uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); + uint256 marketTotalBorrow = totalBorrow[id]; + uint256 borrowRate = market.irm.borrowRate(market); + uint256 accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; } @@ -262,12 +263,12 @@ contract Blue { // Health check. function isHealthy(Market calldata market, Id id, address user) private view returns (bool) { - uint borrowShares = borrowShare[id][user]; + uint256 borrowShares = borrowShare[id][user]; if (borrowShares == 0) return true; // totalBorrowShares[id] > 0 when borrowShares > 0. - uint borrowValue = + uint256 borrowValue = borrowShares.wMul(totalBorrow[id]).wDiv(totalBorrowShares[id]).wMul(market.borrowableOracle.price()); - uint collateralValue = collateral[id][user].wMul(market.collateralOracle.price()); + uint256 collateralValue = collateral[id][user].wMul(market.collateralOracle.price()); return collateralValue.wMul(market.lltv) >= borrowValue; } } diff --git a/src/interfaces/IIrm.sol b/src/interfaces/IIrm.sol index 035dd2e82..791725d83 100644 --- a/src/interfaces/IIrm.sol +++ b/src/interfaces/IIrm.sol @@ -4,5 +4,5 @@ pragma solidity >=0.5.0; import {Market} from "src/Blue.sol"; interface IIrm { - function borrowRate(Market calldata market) external returns (uint); + function borrowRate(Market calldata market) external returns (uint256); } diff --git a/src/interfaces/IOracle.sol b/src/interfaces/IOracle.sol index 5d795f49c..6da75e903 100644 --- a/src/interfaces/IOracle.sol +++ b/src/interfaces/IOracle.sol @@ -2,5 +2,5 @@ pragma solidity >=0.5.0; interface IOracle { - function price() external view returns (uint); + function price() external view returns (uint256); } diff --git a/src/libraries/MathLib.sol b/src/libraries/MathLib.sol index 355290031..0f4f1e8e2 100644 --- a/src/libraries/MathLib.sol +++ b/src/libraries/MathLib.sol @@ -3,19 +3,19 @@ pragma solidity ^0.8.0; /// @notice Maths utils. library MathLib { - uint internal constant WAD = 1e18; + uint256 internal constant WAD = 1e18; - function min(uint x, uint y) internal pure returns (uint z) { + function min(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x < y ? x : y; } /// @dev Rounds towards zero. - function wMul(uint x, uint y) internal pure returns (uint z) { + function wMul(uint256 x, uint256 y) internal pure returns (uint256 z) { z = (x * y) / WAD; } /// @dev Rounds towards zero. - function wDiv(uint x, uint y) internal pure returns (uint z) { + function wDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { z = (x * WAD) / y; } } diff --git a/src/libraries/SafeTransferLib.sol b/src/libraries/SafeTransferLib.sol index d752e79c4..016cd598a 100644 --- a/src/libraries/SafeTransferLib.sol +++ b/src/libraries/SafeTransferLib.sol @@ -8,7 +8,7 @@ import {IERC20} from "src/interfaces/IERC20.sol"; /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { - function safeTransferFrom(IERC20 token, address from, address to, uint amount) internal { + function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly @@ -38,7 +38,7 @@ library SafeTransferLib { require(success, "TRANSFER_FROM_FAILED"); } - function safeTransfer(IERC20 token, address to, uint amount) internal { + function safeTransfer(IERC20 token, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly diff --git a/src/mocks/ERC20Mock.sol b/src/mocks/ERC20Mock.sol index 7e8bff8b9..e0619950b 100644 --- a/src/mocks/ERC20Mock.sol +++ b/src/mocks/ERC20Mock.sol @@ -6,7 +6,7 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; contract ERC20Mock is ERC20 { constructor(string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol, _decimals) {} - function setBalance(address owner, uint amount) external { + function setBalance(address owner, uint256 amount) external { balanceOf[owner] = amount; } } diff --git a/src/mocks/IrmMock.sol b/src/mocks/IrmMock.sol index ebaf35386..ae28dbdcd 100644 --- a/src/mocks/IrmMock.sol +++ b/src/mocks/IrmMock.sol @@ -6,7 +6,7 @@ import {MathLib} from "src/libraries/MathLib.sol"; import "src/Blue.sol"; contract IrmMock is IIrm { - using MathLib for uint; + using MathLib for uint256; Blue public immutable blue; @@ -14,9 +14,9 @@ contract IrmMock is IIrm { blue = Blue(blueInstance); } - function borrowRate(Market calldata market) external view returns (uint) { + function borrowRate(Market calldata market) external view returns (uint256) { Id id = Id.wrap(keccak256(abi.encode(market))); - uint utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); + uint256 utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); // Divide by the number of seconds in a year. // This is a very simple model (to refine later) where x% utilization corresponds to x% APR. diff --git a/src/mocks/OracleMock.sol b/src/mocks/OracleMock.sol index ea45ad6a7..0e260eb4a 100644 --- a/src/mocks/OracleMock.sol +++ b/src/mocks/OracleMock.sol @@ -4,9 +4,9 @@ pragma solidity 0.8.20; import {IOracle} from "src/interfaces/IOracle.sol"; contract OracleMock is IOracle { - uint public price; + uint256 public price; - function setPrice(uint newPrice) external { + function setPrice(uint256 newPrice) external { price = newPrice; } } diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index e301db0d8..a8638c29d 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -13,11 +13,11 @@ import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; import {IrmMock as Irm} from "src/mocks/IrmMock.sol"; contract BlueTest is Test { - using MathLib for uint; + using MathLib for uint256; address private constant BORROWER = address(1234); address private constant LIQUIDATOR = address(5678); - uint private constant LLTV = 0.8 ether; + uint256 private constant LLTV = 0.8 ether; address private constant OWNER = address(0xdead); Blue private blue; @@ -61,39 +61,39 @@ contract BlueTest is Test { borrowableOracle.setPrice(0); collateralOracle.setPrice(1e18); - borrowableAsset.approve(address(blue), type(uint).max); - collateralAsset.approve(address(blue), type(uint).max); + borrowableAsset.approve(address(blue), type(uint256).max); + collateralAsset.approve(address(blue), type(uint256).max); vm.startPrank(BORROWER); - borrowableAsset.approve(address(blue), type(uint).max); - collateralAsset.approve(address(blue), type(uint).max); + borrowableAsset.approve(address(blue), type(uint256).max); + collateralAsset.approve(address(blue), type(uint256).max); vm.stopPrank(); vm.startPrank(LIQUIDATOR); - borrowableAsset.approve(address(blue), type(uint).max); - collateralAsset.approve(address(blue), type(uint).max); + borrowableAsset.approve(address(blue), type(uint256).max); + collateralAsset.approve(address(blue), type(uint256).max); vm.stopPrank(); } // To move to a test utils file later. - function netWorth(address user) internal view returns (uint) { - uint collateralAssetValue = collateralAsset.balanceOf(user).wMul(collateralOracle.price()); - uint borrowableAssetValue = borrowableAsset.balanceOf(user).wMul(borrowableOracle.price()); + function netWorth(address user) internal view returns (uint256) { + uint256 collateralAssetValue = collateralAsset.balanceOf(user).wMul(collateralOracle.price()); + uint256 borrowableAssetValue = borrowableAsset.balanceOf(user).wMul(borrowableOracle.price()); return collateralAssetValue + borrowableAssetValue; } - function supplyBalance(address user) internal view returns (uint) { - uint supplyShares = blue.supplyShare(id, user); + function supplyBalance(address user) internal view returns (uint256) { + uint256 supplyShares = blue.supplyShare(id, user); if (supplyShares == 0) return 0; - uint totalShares = blue.totalSupplyShares(id); - uint totalSupply = blue.totalSupply(id); + uint256 totalShares = blue.totalSupplyShares(id); + uint256 totalSupply = blue.totalSupply(id); return supplyShares.wMul(totalSupply).wDiv(totalShares); } - function borrowBalance(address user) internal view returns (uint) { - uint borrowerShares = blue.borrowShare(id, user); + function borrowBalance(address user) internal view returns (uint256) { + uint256 borrowerShares = blue.borrowShare(id, user); if (borrowerShares == 0) return 0; - uint totalShares = blue.totalBorrowShares(id); - uint totalBorrow = blue.totalBorrow(id); + uint256 totalShares = blue.totalBorrowShares(id); + uint256 totalBorrow = blue.totalBorrow(id); return borrowerShares.wMul(totalBorrow).wDiv(totalShares); } @@ -165,7 +165,7 @@ contract BlueTest is Test { blue.createMarket(marketFuzz); } - function testEnableLltvWhenNotOwner(address attacker, uint newLltv) public { + function testEnableLltvWhenNotOwner(address attacker, uint256 newLltv) public { vm.assume(attacker != OWNER); vm.prank(attacker); @@ -173,7 +173,7 @@ contract BlueTest is Test { blue.enableLltv(newLltv); } - function testEnableLltv(uint newLltv) public { + function testEnableLltv(uint256 newLltv) public { newLltv = bound(newLltv, 0, WAD - 1); vm.prank(OWNER); @@ -182,8 +182,8 @@ contract BlueTest is Test { assertTrue(blue.isLltvEnabled(newLltv)); } - function testEnableLltvShouldFailWhenLltvTooHigh(uint newLltv) public { - newLltv = bound(newLltv, WAD, type(uint).max); + function testEnableLltvShouldFailWhenLltvTooHigh(uint256 newLltv) public { + newLltv = bound(newLltv, WAD, type(uint256).max); vm.prank(OWNER); vm.expectRevert("LLTV too high"); @@ -199,7 +199,7 @@ contract BlueTest is Test { blue.createMarket(marketFuzz); } - function testSupply(uint amount) public { + function testSupply(uint256 amount) public { amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); @@ -210,7 +210,7 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amount, "blue balance"); } - function testBorrow(uint amountLent, uint amountBorrowed) public { + function testBorrow(uint256 amountLent, uint256 amountBorrowed) public { amountLent = bound(amountLent, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); @@ -237,7 +237,7 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed, "blue balance"); } - function testWithdraw(uint amountLent, uint amountWithdrawn, uint amountBorrowed) public { + function testWithdraw(uint256 amountLent, uint256 amountWithdrawn, uint256 amountBorrowed) public { amountLent = bound(amountLent, 1, 2 ** 64); amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); @@ -271,10 +271,10 @@ contract BlueTest is Test { } function testCollateralRequirements( - uint amountCollateral, - uint amountBorrowed, - uint priceCollateral, - uint priceBorrowable + uint256 amountCollateral, + uint256 amountBorrowed, + uint256 priceCollateral, + uint256 priceBorrowable ) public { amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); priceBorrowable = bound(priceBorrowable, 0, 2 ** 64); @@ -292,8 +292,8 @@ contract BlueTest is Test { vm.prank(BORROWER); blue.supplyCollateral(market, amountCollateral); - uint collateralValue = amountCollateral.wMul(priceCollateral); - uint borrowValue = amountBorrowed.wMul(priceBorrowable); + uint256 collateralValue = amountCollateral.wMul(priceCollateral); + uint256 borrowValue = amountBorrowed.wMul(priceBorrowable); if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(LLTV))) { vm.prank(BORROWER); blue.borrow(market, amountBorrowed); @@ -304,7 +304,7 @@ contract BlueTest is Test { } } - function testRepay(uint amountLent, uint amountBorrowed, uint amountRepaid) public { + function testRepay(uint256 amountLent, uint256 amountBorrowed, uint256 amountRepaid) public { amountLent = bound(amountLent, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, amountLent); amountRepaid = bound(amountRepaid, 1, amountBorrowed); @@ -324,7 +324,7 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } - function testSupplyCollateral(uint amount) public { + function testSupplyCollateral(uint256 amount) public { amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); @@ -335,7 +335,7 @@ contract BlueTest is Test { assertEq(collateralAsset.balanceOf(address(blue)), amount, "blue balance"); } - function testWithdrawCollateral(uint amountDeposited, uint amountWithdrawn) public { + function testWithdrawCollateral(uint256 amountDeposited, uint256 amountWithdrawn) public { amountDeposited = bound(amountDeposited, 1, 2 ** 64); amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); @@ -355,15 +355,15 @@ contract BlueTest is Test { assertEq(collateralAsset.balanceOf(address(blue)), amountDeposited - amountWithdrawn, "blue balance"); } - function testLiquidate(uint amountLent) public { + function testLiquidate(uint256 amountLent) public { borrowableOracle.setPrice(1e18); amountLent = bound(amountLent, 1000, 2 ** 64); - uint amountCollateral = amountLent; - uint borrowingPower = amountCollateral.wMul(LLTV); - uint amountBorrowed = borrowingPower.wMul(0.8e18); - uint toSeize = amountCollateral.wMul(LLTV); - uint incentive = WAD + ALPHA.wMul(WAD.wDiv(LLTV) - WAD); + uint256 amountCollateral = amountLent; + uint256 borrowingPower = amountCollateral.wMul(LLTV); + uint256 amountBorrowed = borrowingPower.wMul(0.8e18); + uint256 toSeize = amountCollateral.wMul(LLTV); + uint256 incentive = WAD + ALPHA.wMul(WAD.wDiv(LLTV) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(BORROWER, amountCollateral); @@ -381,31 +381,31 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(2e18); - uint liquidatorNetWorthBefore = netWorth(LIQUIDATOR); + uint256 liquidatorNetWorthBefore = netWorth(LIQUIDATOR); // Liquidate vm.prank(LIQUIDATOR); blue.liquidate(market, BORROWER, toSeize); - uint liquidatorNetWorthAfter = netWorth(LIQUIDATOR); + uint256 liquidatorNetWorthAfter = netWorth(LIQUIDATOR); - uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); - uint expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) + uint256 expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); + uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) - expectedRepaid.wMul(borrowableOracle.price()); assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth"); assertApproxEqAbs(borrowBalance(BORROWER), amountBorrowed - expectedRepaid, 100, "BORROWER balance"); assertEq(blue.collateral(id, BORROWER), amountCollateral - toSeize, "BORROWER collateral"); } - function testRealizeBadDebt(uint amountLent) public { + function testRealizeBadDebt(uint256 amountLent) public { borrowableOracle.setPrice(1e18); amountLent = bound(amountLent, 1000, 2 ** 64); - uint amountCollateral = amountLent; - uint borrowingPower = amountCollateral.wMul(LLTV); - uint amountBorrowed = borrowingPower.wMul(0.8e18); - uint toSeize = amountCollateral; - uint incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lltv) - WAD); + uint256 amountCollateral = amountLent; + uint256 borrowingPower = amountCollateral.wMul(LLTV); + uint256 amountBorrowed = borrowingPower.wMul(0.8e18); + uint256 toSeize = amountCollateral; + uint256 incentive = WAD + ALPHA.wMul(WAD.wDiv(market.lltv) - WAD); borrowableAsset.setBalance(address(this), amountLent); collateralAsset.setBalance(BORROWER, amountCollateral); @@ -423,26 +423,26 @@ contract BlueTest is Test { // Price change borrowableOracle.setPrice(100e18); - uint liquidatorNetWorthBefore = netWorth(LIQUIDATOR); + uint256 liquidatorNetWorthBefore = netWorth(LIQUIDATOR); // Liquidate vm.prank(LIQUIDATOR); blue.liquidate(market, BORROWER, toSeize); - uint liquidatorNetWorthAfter = netWorth(LIQUIDATOR); + uint256 liquidatorNetWorthAfter = netWorth(LIQUIDATOR); - uint expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); - uint expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) + uint256 expectedRepaid = toSeize.wMul(collateralOracle.price()).wDiv(incentive).wDiv(borrowableOracle.price()); + uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.wMul(collateralOracle.price()) - expectedRepaid.wMul(borrowableOracle.price()); assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth"); assertEq(borrowBalance(BORROWER), 0, "BORROWER balance"); assertEq(blue.collateral(id, BORROWER), 0, "BORROWER collateral"); - uint expectedBadDebt = amountBorrowed - expectedRepaid; + uint256 expectedBadDebt = amountBorrowed - expectedRepaid; assertGt(expectedBadDebt, 0, "bad debt"); assertApproxEqAbs(supplyBalance(address(this)), amountLent - expectedBadDebt, 10, "lender supply balance"); } - function testTwoUsersSupply(uint firstAmount, uint secondAmount) public { + function testTwoUsersSupply(uint256 firstAmount, uint256 secondAmount) public { firstAmount = bound(firstAmount, 1, 2 ** 64); secondAmount = bound(secondAmount, 1, 2 ** 64); @@ -507,7 +507,7 @@ contract BlueTest is Test { blue.liquidate(market, address(0), 0); } - function testEmptyMarket(uint amount) public { + function testEmptyMarket(uint256 amount) public { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); From 1c06c0551ed218a14ab009494712fe45a6488c37 Mon Sep 17 00:00:00 2001 From: Quentin Garchery Date: Mon, 10 Jul 2023 12:54:46 +0200 Subject: [PATCH 112/112] refactor: removed cached total supply in accrue interests --- src/Blue.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index d138a8d64..cbd2c939f 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -241,12 +241,10 @@ contract Blue { uint marketTotalBorrow = totalBorrow[id]; if (marketTotalBorrow != 0) { - // The total supply is non-zero when the total borrow is non-zero. - uint marketTotalSupply = totalSupply[id]; uint borrowRate = market.irm.borrowRate(market); uint accruedInterests = marketTotalBorrow.wMul(borrowRate).wMul(block.timestamp - lastUpdate[id]); - totalSupply[id] = marketTotalSupply + accruedInterests; totalBorrow[id] = marketTotalBorrow + accruedInterests; + totalSupply[id] += accruedInterests; } lastUpdate[id] = block.timestamp;