diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34a4a52..353cc5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,15 +29,18 @@ jobs: - name: Run Forge fmt run: | + cd eth-contracts forge fmt --check id: fmt - name: Run Forge build run: | + cd eth-contracts forge build --sizes id: build - name: Run Forge tests run: | + cd eth-contracts forge test -vvv id: test diff --git a/.gitmodules b/.gitmodules index b120396..c4ca283 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,18 @@ [submodule "eth-contracts/lib/forge-std"] path = eth-contracts/lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-foundry-upgrades"] + path = lib/openzeppelin-foundry-upgrades + url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "eth-contracts/lib/openzeppelin-foundry-upgrades"] + path = eth-contracts/lib/openzeppelin-foundry-upgrades + url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades +[submodule "eth-contracts/lib/openzeppelin-contracts-upgradeable"] + path = eth-contracts/lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/.idea/contracts.iml b/.idea/contracts.iml index e2e1d1f..51e91fe 100644 --- a/.idea/contracts.iml +++ b/.idea/contracts.iml @@ -5,6 +5,11 @@ + + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index d9e4aa5..c56669a 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,6 @@ - + \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..b88575e --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +23.7.0 \ No newline at end of file diff --git a/eth-contracts/.npmrc b/eth-contracts/.npmrc new file mode 100644 index 0000000..449691b --- /dev/null +++ b/eth-contracts/.npmrc @@ -0,0 +1 @@ +save-exact=true \ No newline at end of file diff --git a/eth-contracts/README.md b/eth-contracts/README.md index 9265b45..1815291 100644 --- a/eth-contracts/README.md +++ b/eth-contracts/README.md @@ -1,20 +1,5 @@ ## Foundry -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** - -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -## Documentation - -https://book.getfoundry.sh/ - -## Usage - ### Build ```shell diff --git a/eth-contracts/lib/openzeppelin-contracts-upgradeable b/eth-contracts/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..3d5fa5c --- /dev/null +++ b/eth-contracts/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 3d5fa5c24c411112bab47bec25cfa9ad0af0e6e8 diff --git a/eth-contracts/lib/openzeppelin-foundry-upgrades b/eth-contracts/lib/openzeppelin-foundry-upgrades new file mode 160000 index 0000000..cbce1e0 --- /dev/null +++ b/eth-contracts/lib/openzeppelin-foundry-upgrades @@ -0,0 +1 @@ +Subproject commit cbce1e00305e943aa1661d43f41e5ac72c662b07 diff --git a/eth-contracts/package.json b/eth-contracts/package.json index 7d9dace..f8e3099 100644 --- a/eth-contracts/package.json +++ b/eth-contracts/package.json @@ -8,5 +8,10 @@ "format": "forge fmt" }, "author": "Abdulla Faraz ", - "license": "MIT" + "license": "MIT", + "dependencies": { + "@uniswap/sdk-core": "7.5.0", + "@uniswap/v3-core": "1.0.1", + "@uniswap/v3-periphery": "1.4.4" + } } diff --git a/eth-contracts/remappings.txt b/eth-contracts/remappings.txt new file mode 100644 index 0000000..6c4fb35 --- /dev/null +++ b/eth-contracts/remappings.txt @@ -0,0 +1,5 @@ +@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +@uniswap/v3-periphery/=node_modules/@uniswap/v3-periphery/ +@uniswap/v3-core/=node_modules/@uniswap/v3-core/ +@uniswap/v3-sdk/=node_modules/@uniswap/v3-sdk/ diff --git a/eth-contracts/script/Counter.s.sol b/eth-contracts/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/eth-contracts/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/eth-contracts/src/AbstractMultiWalletAccount.sol b/eth-contracts/src/AbstractMultiWalletAccount.sol new file mode 100644 index 0000000..d41f378 --- /dev/null +++ b/eth-contracts/src/AbstractMultiWalletAccount.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; +import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; +import "./lib/AddressSet.sol"; + +/* + * AbstractMultiWalletAccount contract + * + * This contract is a simple contract that allows a user to deposit funds to an + * operator held account and withdraw funds from it. + * + */ +abstract contract AbstractMultiWalletAccount { + /* + * The operator of the account (the fund manager if it's a fund) + * Operator should not be able to withdraw funds from account. + * Operator can swap funds between account after verifying signature. + * Operator can swap tokens to other tokens if permission is given by user. + */ + address public operator; + /* + * The base token of the account (the token in which the account is denominated) + * All funds in the account are in base token. + */ + address public baseToken; + + /* + * The set of tokens held in this contract + */ + using AddressSet for AddressSet.Set; + + AddressSet.Set tokenSet; + /* + * The set of depositor who have funds in this contract + */ + AddressSet.Set depositorSet; + + /* + * This mapping stores user balances for each token (user => token => amount) + */ + mapping(address => mapping(address => uint256)) public wallets; + + /* + * This mapping stores total balances for each token + */ + mapping(address => uint256) public contractTokenBalances; + + /* + * this struct is used to pass parameters to swapOnUniswap function + */ + struct SwapOnUniswapParams { + address tokenAddressIn; + address tokenAddressOut; + address swapRouter; + uint24 poolFee; + uint256 amountIn; + uint256 deadline; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + uint256 estimatedGasFees; // gas fee in base currency + } + + event DepositEvent(address from, address tokenAddress, uint256 amountReceived); + event WithdrawEvent(address to, address tokenAddress, uint256 amountSent); + event SwapSucceeded(uint256 amountOut, address tokenOut, uint256 amountIn, address tokenIn, uint24 poolFee); + + constructor(address _operator, address _baseToken) { + operator = _operator; + baseToken = _baseToken; + tokenSet.insert(_baseToken); + } + + function deposit(uint256 amount) external { + address depositor = msg.sender; + + IERC20 baseTokenContract = IERC20(baseToken); + + // check allowance + uint256 allowance = baseTokenContract.allowance(msg.sender, address(this)); + require(allowance >= amount, "Allowance is not enough"); + + // transfer + bool sent = baseTokenContract.transferFrom(depositor, address(this), amount); + require(sent, "Failed to deposit funds"); + + // update balance + wallets[depositor][baseToken] += amount; + contractTokenBalances[baseToken] += amount; + emit DepositEvent(msg.sender, baseToken, amount); + } + + function withdraw(address tokenAddress) external { + address withdrawer = msg.sender; + uint256 balance = wallets[withdrawer][tokenAddress]; + require(balance > 0, "Insufficient balance"); + + // update balance + wallets[withdrawer][tokenAddress] = 0; + contractTokenBalances[tokenAddress] -= balance; + + IERC20 tokenContract = IERC20(tokenAddress); + + // transfer + bool sent = tokenContract.transfer(withdrawer, balance); + require(sent, "Failed to withdraw funds"); + + emit WithdrawEvent(withdrawer, tokenAddress, balance); + } + + function checkBalance(address user, address tokenAddress) public view returns (uint256) { + return wallets[user][tokenAddress]; + } + + function swapOnUniswap(SwapOnUniswapParams calldata swapParams) external onlyOperator { + require(tokenSet.contains(swapParams.tokenAddressIn), "Token not supported"); + require(contractTokenBalances[swapParams.tokenAddressIn] >= swapParams.amountIn, "Insufficient balance"); + + ISwapRouter swapRouter = ISwapRouter(swapParams.swapRouter); + + uint256 amountInAfterFee = swapParams.amountIn - swapParams.estimatedGasFees; + + ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ + tokenIn: swapParams.tokenAddressIn, + tokenOut: swapParams.tokenAddressOut, + fee: swapParams.poolFee, + recipient: address(this), + deadline: block.timestamp + swapParams.deadline, + amountIn: amountInAfterFee, + amountOutMinimum: swapParams.amountOutMinimum, + sqrtPriceLimitX96: swapParams.sqrtPriceLimitX96 + }); + + uint256 amountOut = swapRouter.exactInputSingle(params); + require(amountOut >= swapParams.amountOutMinimum, "Uniswap swap failed"); + + uint256 depositorCount = depositorSet.size(); + address[] memory depositors = new address[](depositorCount); + depositors = depositorSet.values(); + + uint256 contractTokenBalance = contractTokenBalances[swapParams.tokenAddressIn]; + + for (uint256 i = 0; i < depositorCount; i++) { + address depositor = depositors[i]; + uint256 balance = wallets[depositor][swapParams.tokenAddressIn]; + if (balance > 0) { + // deduct tokenIn from users who have tokenIn + uint256 depositorShare = (balance / contractTokenBalance); + wallets[depositor][swapParams.tokenAddressIn] -= depositorShare * swapParams.amountIn; + // update tokenOut balance for users + wallets[depositor][swapParams.tokenAddressOut] += depositorShare * amountOut; + } + } + // update contractTokenBalances + contractTokenBalances[swapParams.tokenAddressIn] -= swapParams.amountIn; + + // add tokenOut to tokenSet + tokenSet.insert(swapParams.tokenAddressOut); + + // update contractTokenBalances + contractTokenBalances[swapParams.tokenAddressOut] += amountOut; + + // emit event + emit SwapSucceeded( + amountOut, swapParams.tokenAddressOut, swapParams.amountIn, swapParams.tokenAddressIn, swapParams.poolFee + ); + } + + modifier onlyOperator() { + require(msg.sender == operator, "Only operator can call this function"); + _; + } + + function receiveEthBalance() external payable onlyOperator {} + + receive() external payable onlyOperator {} + + fallback() external payable onlyOperator {} + + function withdrawEthBalance() external onlyOperator { + uint256 balance = address(this).balance; + address caller = msg.sender; + (bool sent, bytes memory data) = caller.call{value: balance}(""); + require(sent, "Failed to send Ether"); + } +} diff --git a/eth-contracts/src/Counter.sol b/eth-contracts/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/eth-contracts/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/eth-contracts/src/MultiWalletAccount.sol b/eth-contracts/src/MultiWalletAccount.sol new file mode 100644 index 0000000..4c887ea --- /dev/null +++ b/eth-contracts/src/MultiWalletAccount.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import {AbstractMultiWalletAccount} from "./AbstractMultiWalletAccount.sol"; + +contract MultiWalletAccount is AbstractMultiWalletAccount { + constructor(address _operator, address _baseToken) AbstractMultiWalletAccount(_operator, _baseToken) {} +} diff --git a/eth-contracts/src/lib/AddressSet.sol b/eth-contracts/src/lib/AddressSet.sol new file mode 100644 index 0000000..700606c --- /dev/null +++ b/eth-contracts/src/lib/AddressSet.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +// Copyright (c), 2019 Rob Hitchens + +library AddressSet { + struct Set { + mapping(address => uint256) keyPointers; + address[] keyList; + } + + function insert(Set storage self, address key) internal { + require(key != address(0), "UnorderedKeySet(100) - Key cannot be 0x0"); + require(!contains(self, key), "UnorderedAddressSet(101) - Address (key) already exists in the set."); + self.keyList.push(key); + self.keyPointers[key] = self.keyList.length - 1; + } + + function remove(Set storage self, address key) internal { + require(contains(self, key), "UnorderedKeySet(102) - Address (key) does not exist in the set."); + address keyToMove = self.keyList[size(self) - 1]; + uint256 rowToReplace = self.keyPointers[key]; + self.keyPointers[keyToMove] = rowToReplace; + self.keyList[rowToReplace] = keyToMove; + delete self.keyPointers[key]; + self.keyList.pop(); + } + + function size(Set storage self) internal view returns (uint256) { + return (self.keyList.length); + } + + function contains(Set storage self, address key) internal view returns (bool) { + if (self.keyList.length == 0) return false; + return self.keyList[self.keyPointers[key]] == key; + } + + function keyAtIndex(Set storage self, uint256 index) internal view returns (address) { + return self.keyList[index]; + } + + function values(Set storage self) internal view returns (address[] memory) { + return self.keyList; + } +} diff --git a/eth-contracts/test/Counter.t.sol b/eth-contracts/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/eth-contracts/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/eth-contracts/test/lib/AddressSet.t.sol b/eth-contracts/test/lib/AddressSet.t.sol new file mode 100644 index 0000000..7c1616f --- /dev/null +++ b/eth-contracts/test/lib/AddressSet.t.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {AddressSet} from "../../src/lib/AddressSet.sol"; + +contract AddressSetTest is Test { + using AddressSet for AddressSet.Set; + + AddressSet.Set tokenSet; + + function setUp() public {} + + function test_shouldBeAbleToInsertItems(address randomAddress) public { + if (randomAddress != address(0)) { + tokenSet.insert(randomAddress); + assert(tokenSet.contains(randomAddress)); + } + } + + /// forge-config: default.allow_internal_expect_revert = true + function test_shouldNotBeAbleToInsertZeroAddress() public { + vm.expectRevert("UnorderedKeySet(100) - Key cannot be 0x0"); + tokenSet.insert(address(0)); + } + + /// forge-config: default.allow_internal_expect_revert = true + function test_shouldRevertIfInsertingDuplicateItems(address randomAddress) public { + if (randomAddress != address(0)) { + vm.expectRevert("UnorderedAddressSet(101) - Address (key) already exists in the set."); + tokenSet.insert(randomAddress); + tokenSet.insert(randomAddress); + } + } + + function test_shouldBeAbleToRemoveItems(address randomAddress1, address randomAddress2) public { + if ( + randomAddress1 != randomAddress2 && address(randomAddress1) != address(0) + && address(randomAddress2) != address(0) + ) { + tokenSet.insert(randomAddress1); + tokenSet.insert(randomAddress2); + tokenSet.remove(randomAddress1); + assert(!tokenSet.contains(randomAddress1)); + assert(tokenSet.contains(randomAddress2)); + } + } + + /// forge-config: default.allow_internal_expect_revert = true + function test_shouldRevertIfRemovingNonExistentItems(address randomAddress) public { + if (randomAddress != address(0)) { + vm.expectRevert("UnorderedKeySet(102) - Address (key) does not exist in the set."); + tokenSet.remove(randomAddress); + } + } + + function test_shouldBeAbleToGetSizeAfterInserting(address randomAddress1, address randomAddress2) public { + if (randomAddress1 != randomAddress2 && randomAddress1 != address(0) && randomAddress2 != address(0)) { + tokenSet.insert(randomAddress1); + tokenSet.insert(randomAddress2); + assert(tokenSet.size() == 2); + } + } + + function test_shouldBeAbleToGetSizeAfterRemoving( + address randomAddress1, + address randomAddress2, + address randomAddress3 + ) public { + if ( + randomAddress1 != randomAddress2 && randomAddress2 != randomAddress3 && randomAddress1 != randomAddress3 + && randomAddress1 != address(0) && randomAddress2 != address(0) && randomAddress3 != address(0) + ) { + tokenSet.insert(randomAddress1); + tokenSet.insert(randomAddress2); + tokenSet.insert(randomAddress3); + tokenSet.remove(randomAddress1); + assert(tokenSet.size() == 2); + } + } + + function test_shouldBeAbleToGetSizeAfterRemovingAllItems(address randomAddress1, address randomAddress2) public { + if (randomAddress1 != randomAddress2 && randomAddress1 != address(0) && randomAddress2 != address(0)) { + tokenSet.insert(randomAddress1); + tokenSet.insert(randomAddress2); + tokenSet.remove(randomAddress1); + tokenSet.remove(randomAddress2); + assert(tokenSet.size() == 0); + } + } + + function test_shouldReturnTrueIfAddressExists(address randomAddress) public { + if (randomAddress != address(0)) { + tokenSet.insert(randomAddress); + assert(tokenSet.contains(randomAddress)); + } + } + + function test_shouldReturnFalseIfAddressDoesNotExist(address randomAddress1, address randomAddress2) public { + if (randomAddress1 != address(0) && randomAddress1 != randomAddress2) { + tokenSet.insert(randomAddress1); + assert(!tokenSet.contains(randomAddress2)); + } + } + + function test_shouldBeAbleToReferenceByIndex(address randomAddress1, address randomAddress2) public { + if (randomAddress1 != randomAddress2 && randomAddress1 != address(0) && randomAddress2 != address(0)) { + tokenSet.insert(randomAddress1); + tokenSet.insert(randomAddress2); + assert(tokenSet.keyAtIndex(0) == randomAddress1); + assert(tokenSet.keyAtIndex(1) == randomAddress2); + } + } + + function test_shouldBeAbleToGetValues(address randomAddress1, address randomAddress2) public { + if (randomAddress1 != randomAddress2 && randomAddress1 != address(0) && randomAddress2 != address(0)) { + tokenSet.insert(randomAddress1); + tokenSet.insert(randomAddress2); + address[] memory values = tokenSet.values(); + assert(values.length == 2); + assert(values[0] == randomAddress1); + assert(values[1] == randomAddress2); + } + } +} diff --git a/package.json b/package.json index 1e3d2ac..7ee1711 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,15 @@ "scripts": { "build": "turbo build", "test": "turbo test", - "format": "turbo format" + "format": "turbo format", + "prepare": "husky" }, "author": "Abdulla Faraz ", "license": "MIT", "packageManager": "pnpm@10.4.1", "devDependencies": { + "husky": "9.1.7", + "lint-staged": "15.4.3", "turbo": "2.4.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7477b0f..48e9b55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,14 +8,314 @@ importers: .: devDependencies: + husky: + specifier: 9.1.7 + version: 9.1.7 + lint-staged: + specifier: 15.4.3 + version: 15.4.3 turbo: specifier: 2.4.2 version: 2.4.2 - eth-contracts: {} + eth-contracts: + dependencies: + '@uniswap/sdk-core': + specifier: 7.5.0 + version: 7.5.0 + '@uniswap/v3-core': + specifier: 1.0.1 + version: 1.0.1 + '@uniswap/v3-periphery': + specifier: 1.4.4 + version: 1.4.4 packages: + '@ethersproject/address@5.7.0': + resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} + + '@ethersproject/bignumber@5.7.0': + resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} + + '@ethersproject/bytes@5.7.0': + resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} + + '@ethersproject/constants@5.7.0': + resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} + + '@ethersproject/keccak256@5.7.0': + resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} + + '@ethersproject/logger@5.7.0': + resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} + + '@ethersproject/rlp@5.7.0': + resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} + + '@ethersproject/strings@5.7.0': + resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} + + '@openzeppelin/contracts@3.4.2-solc-0.7': + resolution: {integrity: sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA==} + + '@uniswap/lib@4.0.1-alpha': + resolution: {integrity: sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA==} + engines: {node: '>=10'} + + '@uniswap/sdk-core@7.5.0': + resolution: {integrity: sha512-4eMbQTu+pEO6CGFG+G9M4ACFPephhfNPpRH40+GGgceHzs73OwQ+5v6Fw5e2JGxr/DEJrS/KcJ2Xjmg/QQ3D6Q==} + engines: {node: '>=10'} + + '@uniswap/v2-core@1.0.1': + resolution: {integrity: sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==} + engines: {node: '>=10'} + + '@uniswap/v3-core@1.0.1': + resolution: {integrity: sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ==} + engines: {node: '>=10'} + + '@uniswap/v3-periphery@1.4.4': + resolution: {integrity: sha512-S4+m+wh8HbWSO3DKk4LwUCPZJTpCugIsHrWR86m/OrUyvSqGDTXKFfc2sMuGXCZrD1ZqO3rhQsKgdWg3Hbb2Kw==} + engines: {node: '>=10'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + base64-sol@1.0.1: + resolution: {integrity: sha512-ld3cCNMeXt4uJXmLZBHFGMvVpK9KsLVEhPpFRXnvSVAqABKbuNZg/+dsq3NuM+wxFLb/UrVkz7m1ciWmkMfTbg==} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + + jsbi@3.2.5: + resolution: {integrity: sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lint-staged@15.4.3: + resolution: {integrity: sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + engines: {node: '>=18.0.0'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toformat@2.0.0: + resolution: {integrity: sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ==} + turbo-darwin-64@2.4.2: resolution: {integrity: sha512-HFfemyWB60CJtEvVQj9yby5rkkWw9fLAdLtAPGtPQoU3tKh8t/uzCAZKso2aPVbib9vGUuGbPGoGpaRXdVhj5g==} cpu: [x64] @@ -50,8 +350,295 @@ packages: resolution: {integrity: sha512-Qxi0ioQCxMRUCcHKHZkTnYH8e7XCpNfg9QiJcyfWIc+ZXeaCjzV5rCGlbQlTXMAtI8qgfP8fZADv3CFtPwqdPQ==} hasBin: true + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + snapshots: + '@ethersproject/address@5.7.0': + 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/bignumber@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + bn.js: 5.2.1 + + '@ethersproject/bytes@5.7.0': + dependencies: + '@ethersproject/logger': 5.7.0 + + '@ethersproject/constants@5.7.0': + dependencies: + '@ethersproject/bignumber': 5.7.0 + + '@ethersproject/keccak256@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + js-sha3: 0.8.0 + + '@ethersproject/logger@5.7.0': {} + + '@ethersproject/rlp@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + + '@ethersproject/strings@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + + '@openzeppelin/contracts@3.4.2-solc-0.7': {} + + '@uniswap/lib@4.0.1-alpha': {} + + '@uniswap/sdk-core@7.5.0': + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/strings': 5.7.0 + big.js: 5.2.2 + decimal.js-light: 2.5.1 + jsbi: 3.2.5 + tiny-invariant: 1.3.3 + toformat: 2.0.0 + + '@uniswap/v2-core@1.0.1': {} + + '@uniswap/v3-core@1.0.1': {} + + '@uniswap/v3-periphery@1.4.4': + dependencies: + '@openzeppelin/contracts': 3.4.2-solc-0.7 + '@uniswap/lib': 4.0.1-alpha + '@uniswap/v2-core': 1.0.1 + '@uniswap/v3-core': 1.0.1 + base64-sol: 1.0.1 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@6.1.0: {} + + ansi-styles@6.2.1: {} + + base64-sol@1.0.1: {} + + big.js@5.2.2: {} + + bn.js@5.2.1: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + chalk@5.4.1: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + colorette@2.0.20: {} + + commander@13.1.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decimal.js-light@2.5.1: {} + + emoji-regex@10.4.0: {} + + environment@1.1.0: {} + + eventemitter3@5.0.1: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + get-east-asian-width@1.3.0: {} + + get-stream@8.0.1: {} + + human-signals@5.0.0: {} + + husky@9.1.7: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-number@7.0.0: {} + + is-stream@3.0.0: {} + + isexe@2.0.0: {} + + js-sha3@0.8.0: {} + + jsbi@3.2.5: {} + + lilconfig@3.1.3: {} + + lint-staged@15.4.3: + dependencies: + chalk: 5.4.1 + commander: 13.1.0 + debug: 4.4.0 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.2.5 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.7.0 + transitivePeerDependencies: + - supports-color + + listr2@8.2.5: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + merge-stream@2.0.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + ms@2.1.3: {} + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + picomatch@2.3.1: {} + + pidtree@0.6.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + rfdc@1.4.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-final-newline@3.0.0: {} + + tiny-invariant@1.3.3: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toformat@2.0.0: {} + turbo-darwin-64@2.4.2: optional: true @@ -78,3 +665,15 @@ snapshots: turbo-linux-arm64: 2.4.2 turbo-windows-64: 2.4.2 turbo-windows-arm64: 2.4.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + yaml@2.7.0: {}