diff --git a/.env.empty b/.env.empty deleted file mode 100644 index 5c6b428..0000000 --- a/.env.empty +++ /dev/null @@ -1,3 +0,0 @@ -ALICE_PK= -BOB_PK= -ETH_RPC_URL=wss://ropsten.infura.io/ws \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8c9ff05 --- /dev/null +++ b/.env.example @@ -0,0 +1,43 @@ +# Deployer (DO NOT COMMIT REAL KEYS) +DEPLOYER_PRIVATE_KEY=0x... + +# CREATE2 Salt (32 bytes, mined for desired vanity address) +ETOMIC_SWAP_V1_CREATE2_SALT=0x... + +# ============ RPC URLs ============ +# Testnets +SEPOLIA_RPC_URL= +BSC_TESTNET_RPC_URL= +POLYGON_MUMBAI_RPC_URL= +AVALANCHE_TESTNET_RPC_URL= +FANTOM_TESTNET_RPC_URL= +GLEEC_TESTNET_RPC_URL= + +# Mainnets +MAINNET_RPC_URL= +BSC_RPC_URL= +POLYGON_RPC_URL= +AVALANCHE_RPC_URL= +ARBITRUM_RPC_URL= +BASE_RPC_URL= +KCC_RPC_URL= +MOONRIVER_RPC_URL= +MOONBEAM_RPC_URL= +FANTOM_RPC_URL= +HARMONY_RPC_URL= +ETC_RPC_URL= +RSK_RPC_URL= +EWC_RPC_URL= +SMARTBCH_RPC_URL= +UBIQ_RPC_URL= +QTUM_RPC_URL= + +# ============ BLOCK EXPLORER API KEYS ============ +ETHERSCAN_API_KEY= +BSCSCAN_API_KEY= +POLYGONSCAN_API_KEY= +SNOWTRACE_API_KEY= +ARBISCAN_API_KEY= +BASESCAN_API_KEY= +MOONSCAN_API_KEY= +FTMSCAN_API_KEY= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 41f0d04..72a012d 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,9 @@ typings/ .env .idea -merge \ No newline at end of file +merge + +artifacts +cache + +ignition/deployments/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 941bb26..40d61e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,10 @@ services: - docker before_install: - - cp .env.empty .env - docker-compose build - docker-compose up -d script: - docker-compose exec workspace yarn - - docker-compose exec workspace truffle test + - docker-compose exec workspace npx hardhat test - docker-compose down \ No newline at end of file diff --git a/DEPLOYMENTS.md b/DEPLOYMENTS.md new file mode 100644 index 0000000..37e4cfa --- /dev/null +++ b/DEPLOYMENTS.md @@ -0,0 +1,97 @@ +# EtomicSwap V1 (SafeERC20) Deployments + +## Deployed Address (all chains) + +``` +0x61EEC68Cf64d1b31e41EA713356De2563fB6D3F1 +``` + +Same address on every EVM chain via deterministic CREATE2 deployment. + +## Live Deployments + +| Chain | Chain ID | TX Hash | Explorer | +|-------|----------|---------|----------| +| Ethereum | 1 | `0xd78d0b7138fb1657da3d61e4db664d08b85fbcbb92ebf457cc8e3fa1f4d45a5c` | [etherscan.io](https://etherscan.io/address/0x61EEC68Cf64d1b31e41EA713356De2563fB6D3F1#code) | +| Sepolia (testnet) | 11155111 | `0x0ce7879bdb6546e1a806bab1a8c09f990622bab69ae252cb79c5a04418a4150b` | [sepolia.etherscan.io](https://sepolia.etherscan.io/address/0x61EEC68Cf64d1b31e41EA713356De2563fB6D3F1#code) | +| GLEEC testnet | 11169 | `0x7b4c6ba0a553ed83f5f2c9149ed03bdb1e1de28577a358ffd883f741db472307` | [explorer.gleec.dev](https://explorer.gleec.dev/address/0x51d9EfFc20F6965bc8DFD37E797ac52a72fcdb9D) | + +> **Note:** GLEEC testnet uses a non-CREATE2 deployment (address `0x51d9EfFc20F6965bc8DFD37E797ac52a72fcdb9D`) because CreateX is not available on this chain. + +## Deployment Steps + +The contract is deployed using [Hardhat Ignition](https://hardhat.org/ignition) with the CREATE2 strategy, which uses the [CreateX](https://github.com/pcaversaccio/createx) factory (`0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed`) to produce deterministic addresses across chains. + +### Prerequisites + +1. Copy `.env.example` to `.env` and fill in: + - `DEPLOYER_PRIVATE_KEY` — the deployer wallet (`0x936c269a7378Ac408EF6EAB5770d29EA8EC6ecDd`) + - `ETOMIC_SWAP_V1_CREATE2_SALT` — the mined salt (see below) + - `ETHERSCAN_API_KEY` — for contract verification + - The RPC URL env var for the target chain (e.g., `BSC_RPC_URL`) + +2. Fund the deployer address with native gas on the target chain (~1.2M gas needed). + +3. Install dependencies: + ```bash + yarn install + ``` + +### Deploy to a new chain + +```bash +npx hardhat ignition deploy ignition/modules/EtomicSwapV1.js --network --strategy create2 +``` + +Where `` matches a key in `hardhat.config.js` `networks` (e.g., `bsc`, `polygon`, `avalanche`). + +### Verify contract source + +```bash +npx hardhat verify --network 0x61EEC68Cf64d1b31e41EA713356De2563fB6D3F1 +``` + +For chains using Etherscan V2, the single `ETHERSCAN_API_KEY` works. For chains with custom explorers (KCC, EWC), see `customChains` in `hardhat.config.js`. + +### Adding a new chain + +1. Add the network to `hardhat.config.js`: + ```js + newchain: networkConfig(, "NEWCHAIN_RPC_URL"), + ``` +2. Add the RPC URL to `.env` +3. Verify that [CreateX is deployed](https://github.com/pcaversaccio/createx#deployments) on the target chain +4. Deploy and verify using the commands above +5. Update this file with the TX hash and explorer link + +## Pending Deployments + +| Chain | Chain ID | Network Name | RPC Env Var | +|-------|----------|--------------|-------------| +| BSC | 56 | `bsc` | `BSC_RPC_URL` | +| Polygon | 137 | `polygon` | `POLYGON_RPC_URL` | +| Avalanche | 43114 | `avalanche` | `AVALANCHE_RPC_URL` | +| Arbitrum One | 42161 | `arbitrumOne` | `ARBITRUM_RPC_URL` | +| Base | 8453 | `base` | `BASE_RPC_URL` | +| Fantom | 250 | `opera` | `FANTOM_RPC_URL` | +| Moonriver | 1285 | `moonriver` | `MOONRIVER_RPC_URL` | +| Moonbeam | 1284 | `moonbeam` | `MOONBEAM_RPC_URL` | +| KCC | 321 | `kcc` | `KCC_RPC_URL` | +| Harmony | 1666600000 | `harmony` | `HARMONY_RPC_URL` | +| ETC | 61 | `etc` | `ETC_RPC_URL` | +| RSK | 30 | `rsk` | `RSK_RPC_URL` | +| EWC | 246 | `ewc` | `EWC_RPC_URL` | +| SmartBCH | 10000 | `smartbch` | `SMARTBCH_RPC_URL` | +| Ubiq | 8 | `ubiq` | `UBIQ_RPC_URL` | +| Qtum | 3888 | `qtum` | `QTUM_RPC_URL` | + +## Background + +- **Contract**: `EtomicSwap.sol` with OpenZeppelin SafeERC20 (enables USDT and other non-standard ERC20 tokens) +- **Deployer**: `0x936c269a7378Ac408EF6EAB5770d29EA8EC6ecDd` +- **CreateX Factory**: [`0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed`](https://github.com/pcaversaccio/createx) +- **Salt**: `0x936c269a7378ac408ef6eab5770d29ea8ec6ecdd0050a3d70fd56cd902144e36` + - Bytes 0-19: deployer address (permissioned salt) + - Byte 20: `0x00` (cross-chain mode — same address on all chains) + - Bytes 21-31: mined entropy via [createXcrunch](https://github.com/pcaversaccio/createx-crunch) +- **Previous V1 Contract** (no SafeERC20): `0x24ABE4c71FC658C91313b6552cd40cD808b3Ea80` diff --git a/README.md b/README.md index 72516ed..a712c04 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Etomic Swap Smart Contracts for BarterDex platform. +# Etomic Swap Smart Contracts for Komodo SDK. [![Build Status](https://travis-ci.org/artemii235/etomic-swap.svg?branch=master)](https://travis-ci.org/artemii235/etomic-swap) -Etomic swap Smart Contract is implemented to support ETH and ERC20 atomic swaps on BarterDex platform. +Etomic swap Smart Contract is implemented to support ETH and ERC20 atomic swaps on Komodo SDK. Please note that this project is not production ready yet! ## Swap workflow @@ -21,12 +21,17 @@ Despite example shows swap of ETH/ERC20 this approach will work also for ETH/ERC ## How to setup dev environment? +**Note:** +This setup supports both Docker Compose V1 (using `docker-compose` commands) and Docker Compose V2 (using `docker compose` commands). +Docker Compose V2 is preferable, as [from July 2023 Compose V1 stopped receiving updates](https://docs.docker.com/compose/). + 1. Install docker. -1. Run `docker-compose build`. -1. `cp .env.empty .env`. -1. Start containers `docker-compose up -d`. -1. Install project dependencies: `docker-compose exec workspace yarn`. -1. To run tests: `docker-compose exec workspace truffle test`. +1. Run `docker compose build`. +1. Start containers `docker compose up -d`. +1. Install project dependencies: `docker compose exec workspace yarn`. +1. To run tests: `docker compose exec workspace npx hardhat test`. +1. To clean artifacts and cache: `docker compose exec workspace npx hardhat clean`. +1. Stop containers `docker compose down`. ## Related links @@ -34,6 +39,22 @@ Despite example shows swap of ETH/ERC20 this approach will work also for ETH/ERC ## Useful links for smart contracts development -1. Truffle suite - https://github.com/trufflesuite/truffle -1. Ganache-cli (EthereumJS Testrpc) - https://github.com/trufflesuite/ganache-cli -1. Zeppelin Solidity - https://github.com/OpenZeppelin/zeppelin-solidity +1. **Hardhat:** An Ethereum development environment. It facilitates building, testing, and deploying smart contracts. - https://hardhat.org +1. **Ethers.js:** A complete Ethereum library and wallet implementation in JavaScript. - https://github.com/ethers-io/ethers.js +1. **OpenZeppelin Contracts:** A library for secure smart contract development. It provides reusable contracts which are secure and tested. - https://github.com/OpenZeppelin/openzeppelin-contracts + +## Contribution Guide + +- Run Docker tests to ensure that the project is set up successfully. +- Write tests for new contracts and functionalities. +- Run tests to confirm that new implementations work correctly. +- Format Solidity code before opening a pull request (PR). For formatting, you can use Remix Online IDE - https://remix.ethereum.org/ + +## Where Can I Write Solidity Code? + +### Notes for Those Without an IDE: +Using Remix Online IDE is sufficient. There's no need to install anything locally. + +### Notes for JetBrains or Visual Studio Code (VSCode) Users: +- These IDEs offer Solidity and HardHat plugins, which can simplify your workflow. However, Remix Online IDE is also a viable option. +- To index JavaScript code, execute the Docker commands as mentioned. Necessary dependencies will be downloaded, enabling the IDE to index the rest of the code. \ No newline at end of file diff --git a/contracts/Erc1155Token.sol b/contracts/Erc1155Token.sol new file mode 100644 index 0000000..490063e --- /dev/null +++ b/contracts/Erc1155Token.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +contract Erc1155Token is ERC1155 { + constructor(string memory tokenUri) ERC1155(tokenUri) { + uint256 tokenId = 1; + uint256 amount = 3; + _mint(msg.sender, tokenId, amount, ""); + } +} diff --git a/contracts/Erc721Token.sol b/contracts/Erc721Token.sol new file mode 100644 index 0000000..01ee727 --- /dev/null +++ b/contracts/Erc721Token.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract Erc721Token is ERC721 { + constructor(string memory tokenName, string memory tokenSymbol) + ERC721(tokenName, tokenSymbol) + { + uint256 tokenId = 1; + _mint(msg.sender, tokenId); + } +} diff --git a/contracts/EtomicSwap.sol b/contracts/EtomicSwap.sol index 3a1ccec..5456af0 100644 --- a/contracts/EtomicSwap.sol +++ b/contracts/EtomicSwap.sol @@ -1,11 +1,16 @@ -pragma solidity ^0.5.0; -import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.33; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; contract EtomicSwap { + using SafeERC20 for IERC20; + enum PaymentState { Uninitialized, PaymentSent, - ReceivedSpent, + ReceiverSpent, SenderRefunded } @@ -15,125 +20,149 @@ contract EtomicSwap { PaymentState state; } - mapping (bytes32 => Payment) public payments; + mapping(bytes32 => Payment) public payments; event PaymentSent(bytes32 id); event ReceiverSpent(bytes32 id, bytes32 secret); event SenderRefunded(bytes32 id); - constructor() public { } + constructor() {} function ethPayment( - bytes32 _id, - address _receiver, - bytes20 _secretHash, - uint64 _lockTime + bytes32 id, + address receiver, + bytes20 secretHash, + uint64 lockTime ) external payable { - require(_receiver != address(0) && msg.value > 0 && payments[_id].state == PaymentState.Uninitialized); + require(receiver != address(0), "Receiver cannot be the zero address"); + require(msg.value > 0, "Payment amount must be greater than 0"); + require( + payments[id].state == PaymentState.Uninitialized, + "ETH payment already initialized" + ); - bytes20 paymentHash = ripemd160(abi.encodePacked( - _receiver, + bytes20 paymentHash = ripemd160( + abi.encodePacked( + receiver, msg.sender, - _secretHash, + secretHash, address(0), msg.value - )); - - payments[_id] = Payment( - paymentHash, - _lockTime, - PaymentState.PaymentSent + ) ); - emit PaymentSent(_id); + payments[id] = Payment(paymentHash, lockTime, PaymentState.PaymentSent); + + emit PaymentSent(id); } function erc20Payment( - bytes32 _id, - uint256 _amount, - address _tokenAddress, - address _receiver, - bytes20 _secretHash, - uint64 _lockTime - ) external payable { - require(_receiver != address(0) && _amount > 0 && payments[_id].state == PaymentState.Uninitialized); + bytes32 id, + uint256 amount, + address tokenAddress, + address receiver, + bytes20 secretHash, + uint64 lockTime + ) external { + require(receiver != address(0), "Receiver cannot be the zero address"); + require(tokenAddress != address(0), "Token address cannot be zero"); + require(amount > 0, "Payment amount must be greater than 0"); + require( + payments[id].state == PaymentState.Uninitialized, + "ERC20 payment already initialized" + ); - bytes20 paymentHash = ripemd160(abi.encodePacked( - _receiver, + bytes20 paymentHash = ripemd160( + abi.encodePacked( + receiver, msg.sender, - _secretHash, - _tokenAddress, - _amount - )); - - payments[_id] = Payment( - paymentHash, - _lockTime, - PaymentState.PaymentSent + secretHash, + tokenAddress, + amount + ) ); - IERC20 token = IERC20(_tokenAddress); - require(token.transferFrom(msg.sender, address(this), _amount)); - emit PaymentSent(_id); + payments[id] = Payment(paymentHash, lockTime, PaymentState.PaymentSent); + + // Emitting the event before making the external call + emit PaymentSent(id); + + // Now performing the external interaction + IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), amount); } function receiverSpend( - bytes32 _id, - uint256 _amount, - bytes32 _secret, - address _tokenAddress, - address _sender + bytes32 id, + uint256 amount, + bytes32 secret, + address tokenAddress, + address sender ) external { - require(payments[_id].state == PaymentState.PaymentSent); - - bytes20 paymentHash = ripemd160(abi.encodePacked( + // Checks + require( + payments[id].state == PaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + bytes20 paymentHash = ripemd160( + abi.encodePacked( msg.sender, - _sender, - ripemd160(abi.encodePacked(sha256(abi.encodePacked(_secret)))), - _tokenAddress, - _amount - )); - - require(paymentHash == payments[_id].paymentHash); - payments[_id].state = PaymentState.ReceivedSpent; - if (_tokenAddress == address(0)) { - msg.sender.transfer(_amount); + sender, + ripemd160(abi.encodePacked(sha256(abi.encodePacked(secret)))), + tokenAddress, + amount + ) + ); + require(paymentHash == payments[id].paymentHash, "Invalid paymentHash"); + + // Effects + payments[id].state = PaymentState.ReceiverSpent; + + // Event Emission + emit ReceiverSpent(id, secret); + + // Interactions + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); } else { - IERC20 token = IERC20(_tokenAddress); - require(token.transfer(msg.sender, _amount)); + IERC20(tokenAddress).safeTransfer(msg.sender, amount); } - - emit ReceiverSpent(_id, _secret); } function senderRefund( - bytes32 _id, - uint256 _amount, - bytes20 _paymentHash, - address _tokenAddress, - address _receiver + bytes32 id, + uint256 amount, + bytes20 secretHash, + address tokenAddress, + address receiver ) external { - require(payments[_id].state == PaymentState.PaymentSent); + require( + payments[id].state == PaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); - bytes20 paymentHash = ripemd160(abi.encodePacked( - _receiver, + bytes20 paymentHash = ripemd160( + abi.encodePacked( + receiver, msg.sender, - _paymentHash, - _tokenAddress, - _amount - )); + secretHash, + tokenAddress, + amount + ) + ); + require(paymentHash == payments[id].paymentHash, "Invalid paymentHash"); + require( + block.timestamp >= payments[id].lockTime, + "Current timestamp didn't exceed payment lock time" + ); - require(paymentHash == payments[_id].paymentHash && now >= payments[_id].lockTime); + payments[id].state = PaymentState.SenderRefunded; - payments[_id].state = PaymentState.SenderRefunded; + emit SenderRefunded(id); - if (_tokenAddress == address(0)) { - msg.sender.transfer(_amount); + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); } else { - IERC20 token = IERC20(_tokenAddress); - require(token.transfer(msg.sender, _amount)); + IERC20(tokenAddress).safeTransfer(msg.sender, amount); } - - emit SenderRefunded(_id); } } diff --git a/contracts/EtomicSwapMakerNftV2.sol b/contracts/EtomicSwapMakerNftV2.sol new file mode 100644 index 0000000..4bbbb2d --- /dev/null +++ b/contracts/EtomicSwapMakerNftV2.sol @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +contract EtomicSwapMakerNftV2 is ERC165, IERC1155Receiver, IERC721Receiver { + enum MakerPaymentState { + Uninitialized, + PaymentSent, + TakerSpent, + MakerRefunded + } + + struct MakerPayment { + bytes20 paymentHash; + uint32 paymentLockTime; + MakerPaymentState state; + } + + event MakerPaymentSent(bytes32 id); + event MakerPaymentSpent(bytes32 id); + event MakerPaymentRefundedTimelock(bytes32 id); + event MakerPaymentRefundedSecret(bytes32 id); + + mapping(bytes32 => MakerPayment) public makerPayments; + + function spendErc721MakerPayment( + bytes32 id, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress, + uint256 tokenId + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + // Check if the function caller is an externally owned account (EOA) + require(msg.sender == tx.origin, "Caller must be an EOA"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.sender, + maker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress, + tokenId + ) + ); + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + // Effects + makerPayments[id].state = MakerPaymentState.TakerSpent; + + // Event Emission + emit MakerPaymentSpent(id); + + // Interactions + IERC721 token = IERC721(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId); + } + + function spendErc1155MakerPayment( + bytes32 id, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress, + uint256 tokenId, + uint256 amount + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + // Check if the function caller is an externally owned account (EOA) + require(msg.sender == tx.origin, "Caller must be an EOA"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.sender, + maker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress, + tokenId, + amount + ) + ); + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + // Effects + makerPayments[id].state = MakerPaymentState.TakerSpent; + + // Event Emission + emit MakerPaymentSpent(id); + + // Interactions + IERC1155 token = IERC1155(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + } + + function refundErc721MakerPaymentTimelock( + bytes32 id, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress, + tokenId + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + require( + block.timestamp >= makerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedTimelock(id); + + IERC721 token = IERC721(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId); + } + + function refundErc1155MakerPaymentTimelock( + bytes32 id, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId, + uint256 amount + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress, + tokenId, + amount + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + require( + block.timestamp >= makerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedTimelock(id); + + // Interactions + IERC1155 token = IERC1155(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + } + + function refundErc721MakerPaymentSecret( + bytes32 id, + address taker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress, + tokenId + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedSecret(id); + + IERC721 token = IERC721(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId); + } + + function refundErc1155MakerPaymentSecret( + bytes32 id, + address taker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId, + uint256 amount + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress, + tokenId, + amount + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedSecret(id); + + IERC1155 token = IERC1155(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + } + + struct HTLCParams { + bytes32 id; + address taker; + address tokenAddress; + bytes32 takerSecretHash; + bytes32 makerSecretHash; + uint32 paymentLockTime; + } + + function onERC1155Received( + address operator, + address from, + uint256 tokenId, + uint256 value, + bytes calldata data + ) external override returns (bytes4) { + // Decode the data to extract HTLC parameters + HTLCParams memory params = abi.decode(data, (HTLCParams)); + + require( + makerPayments[params.id].state == MakerPaymentState.Uninitialized, + "Maker ERC1155 payment must be Uninitialized" + ); + require(params.taker != address(0), "Taker must not be zero address"); + require( + params.tokenAddress != address(0), + "Token must not be zero address" + ); + require( + msg.sender == params.tokenAddress, + "Token address does not match sender" + ); + require(operator == from, "Operator must be the sender"); + require(value > 0, "Value must be greater than 0"); + require(!isContract(params.taker), "Taker cannot be a contract"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + params.taker, + from, + params.takerSecretHash, + params.makerSecretHash, + params.tokenAddress, + tokenId, + value + ) + ); + + makerPayments[params.id] = MakerPayment( + paymentHash, + params.paymentLockTime, + MakerPaymentState.PaymentSent + ); + emit MakerPaymentSent(params.id); + + // Return this magic value to confirm receipt of ERC1155 token + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, /* operator */ + address, /* from */ + uint256[] calldata, /* ids */ + uint256[] calldata, /* values */ + bytes calldata /* data */ + ) external pure override returns (bytes4) { + revert("Batch transfers not supported"); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC165, IERC165) + returns (bool) + { + return + interfaceId == type(IERC1155Receiver).interfaceId || + interfaceId == type(IERC721Receiver).interfaceId || + super.supportsInterface(interfaceId); + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external override returns (bytes4) { + // Decode the data to extract HTLC parameters + HTLCParams memory params = abi.decode(data, (HTLCParams)); + + require( + makerPayments[params.id].state == MakerPaymentState.Uninitialized, + "Maker ERC721 payment must be Uninitialized" + ); + require(params.taker != address(0), "Taker must not be zero address"); + require( + params.tokenAddress != address(0), + "Token must not be zero address" + ); + require( + msg.sender == params.tokenAddress, + "Token address does not match sender" + ); + require(operator == from, "Operator must be the sender"); + require(!isContract(params.taker), "Taker cannot be a contract"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + params.taker, + from, + params.takerSecretHash, + params.makerSecretHash, + params.tokenAddress, + tokenId + ) + ); + + makerPayments[params.id] = MakerPayment( + paymentHash, + params.paymentLockTime, + MakerPaymentState.PaymentSent + ); + emit MakerPaymentSent(params.id); + + // Return this magic value to confirm receipt of ERC721 token + return this.onERC721Received.selector; + } + + function isContract(address account) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(account) + } + return size > 0; + } +} diff --git a/contracts/EtomicSwapMakerV2.sol b/contracts/EtomicSwapMakerV2.sol new file mode 100644 index 0000000..245cab7 --- /dev/null +++ b/contracts/EtomicSwapMakerV2.sol @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract EtomicSwapMakerV2 { + using SafeERC20 for IERC20; + + enum MakerPaymentState { + Uninitialized, + PaymentSent, + TakerSpent, + MakerRefunded + } + + struct MakerPayment { + bytes20 paymentHash; + uint32 paymentLockTime; + MakerPaymentState state; + } + + event MakerPaymentSent(bytes32 id); + event MakerPaymentSpent(bytes32 id); + event MakerPaymentRefundedTimelock(bytes32 id); + event MakerPaymentRefundedSecret(bytes32 id); + + mapping(bytes32 => MakerPayment) public makerPayments; + + function ethMakerPayment( + bytes32 id, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 paymentLockTime + ) external payable { + require( + makerPayments[id].state == MakerPaymentState.Uninitialized, + "Maker payment is already initialized" + ); + require(taker != address(0), "Taker must not be zero address"); + require(msg.value > 0, "ETH value must be greater than zero"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.value, + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + address(0) + ) + ); + + makerPayments[id] = MakerPayment( + paymentHash, + paymentLockTime, + MakerPaymentState.PaymentSent + ); + + emit MakerPaymentSent(id); + } + + function erc20MakerPayment( + bytes32 id, + uint256 amount, + address tokenAddress, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 paymentLockTime + ) external { + require( + makerPayments[id].state == MakerPaymentState.Uninitialized, + "Maker payment is already initialized" + ); + require(amount > 0, "Amount must not be zero"); + require(taker != address(0), "Taker must not be zero address"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + makerPayments[id] = MakerPayment( + paymentHash, + paymentLockTime, + MakerPaymentState.PaymentSent + ); + + emit MakerPaymentSent(id); + + // Now performing the external interaction + IERC20 token = IERC20(tokenAddress); + token.safeTransferFrom(msg.sender, address(this), amount); + } + + function spendMakerPayment( + bytes32 id, + uint256 amount, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + msg.sender, + maker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress + ) + ); + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.TakerSpent; + + emit MakerPaymentSpent(id); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + } + } + + function refundMakerPaymentTimelock( + bytes32 id, + uint256 amount, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + require( + block.timestamp >= makerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedTimelock(id); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + } + } + + function refundMakerPaymentSecret( + bytes32 id, + uint256 amount, + address taker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + taker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedSecret(id); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + } + } +} diff --git a/contracts/EtomicSwapNft.sol b/contracts/EtomicSwapNft.sol new file mode 100644 index 0000000..e1f5df0 --- /dev/null +++ b/contracts/EtomicSwapNft.sol @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +contract EtomicSwapNft is ERC165, IERC1155Receiver, IERC721Receiver { + using SafeERC20 for IERC20; + + enum MakerPaymentState { + Uninitialized, + PaymentSent, + TakerSpent, + MakerRefunded + } + + struct MakerPayment { + bytes20 paymentHash; + uint32 paymentLockTime; + MakerPaymentState state; + } + + event MakerPaymentSent(bytes32 id); + event MakerPaymentSpent(bytes32 id); + event MakerPaymentRefundedTimelock(bytes32 id); + event MakerPaymentRefundedSecret(bytes32 id); + + mapping(bytes32 => MakerPayment) public makerPayments; + + enum TakerPaymentState { + Uninitialized, + PaymentSent, + TakerApproved, + MakerSpent, + TakerRefunded + } + + struct TakerPayment { + bytes20 paymentHash; + uint32 preApproveLockTime; + uint32 paymentLockTime; + TakerPaymentState state; + } + + event TakerPaymentSent(bytes32 id); + event TakerPaymentApproved(bytes32 id); + event TakerPaymentSpent(bytes32 id, bytes32 secret); + event TakerPaymentRefundedSecret(bytes32 id, bytes32 secret); + event TakerPaymentRefundedTimelock(bytes32 id); + + mapping(bytes32 => TakerPayment) public takerPayments; + + address public immutable dexFeeAddress; + + constructor(address feeAddress) { + require( + feeAddress != address(0), + "feeAddress must not be zero address" + ); + + dexFeeAddress = feeAddress; + } + + function spendErc721MakerPayment( + bytes32 id, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress, + uint256 tokenId + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + // Check if the function caller is an externally owned account (EOA) + require(msg.sender == tx.origin, "Caller must be an EOA"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.sender, + maker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress, + tokenId + ) + ); + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + // Effects + makerPayments[id].state = MakerPaymentState.TakerSpent; + + // Event Emission + emit MakerPaymentSpent(id); + + // Interactions + IERC721 token = IERC721(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId); + } + + function spendErc1155MakerPayment( + bytes32 id, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress, + uint256 tokenId, + uint256 amount + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + // Check if the function caller is an externally owned account (EOA) + require(msg.sender == tx.origin, "Caller must be an EOA"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.sender, + maker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress, + tokenId, + amount + ) + ); + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + // Effects + makerPayments[id].state = MakerPaymentState.TakerSpent; + + // Event Emission + emit MakerPaymentSpent(id); + + // Interactions + IERC1155 token = IERC1155(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + } + + function refundErc721MakerPaymentTimelock( + bytes32 id, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress, + tokenId + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + require( + block.timestamp >= makerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedTimelock(id); + + IERC721 token = IERC721(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId); + } + + function refundErc1155MakerPaymentTimelock( + bytes32 id, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId, + uint256 amount + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress, + tokenId, + amount + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + require( + block.timestamp >= makerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedTimelock(id); + + // Interactions + IERC1155 token = IERC1155(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + } + + function refundErc721MakerPaymentSecret( + bytes32 id, + address taker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress, + tokenId + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedSecret(id); + + IERC721 token = IERC721(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId); + } + + function refundErc1155MakerPaymentSecret( + bytes32 id, + address taker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress, + uint256 tokenId, + uint256 amount + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + taker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress, + tokenId, + amount + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedSecret(id); + + IERC1155 token = IERC1155(tokenAddress); + token.safeTransferFrom(address(this), msg.sender, tokenId, amount, ""); + } + + function ethTakerPayment( + bytes32 id, + uint256 dexFee, + address receiver, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 preApproveLockTime, + uint32 paymentLockTime + ) external payable { + require( + takerPayments[id].state == TakerPaymentState.Uninitialized, + "Taker payment is already initialized" + ); + require(receiver != address(0), "Receiver must not be zero address"); + require(msg.value > 0, "ETH value must be greater than zero"); + require(msg.value > dexFee, "ETH value must be greater than dex fee"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.value - dexFee, + dexFee, + receiver, + msg.sender, + takerSecretHash, + makerSecretHash, + address(0) + ) + ); + + takerPayments[id] = TakerPayment( + paymentHash, + preApproveLockTime, + paymentLockTime, + TakerPaymentState.PaymentSent + ); + + emit TakerPaymentSent(id); + } + + function erc20TakerPayment( + bytes32 id, + uint256 amount, + uint256 dexFee, + address tokenAddress, + address receiver, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 preApproveLockTime, + uint32 paymentLockTime + ) external { + require( + takerPayments[id].state == TakerPaymentState.Uninitialized, + "ERC20 v2 payment is already initialized" + ); + require(amount > 0, "Amount must not be zero"); + require(dexFee > 0, "Dex fee must not be zero"); + require(receiver != address(0), "Receiver must not be zero address"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + receiver, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + takerPayments[id] = TakerPayment( + paymentHash, + preApproveLockTime, + paymentLockTime, + TakerPaymentState.PaymentSent + ); + + emit TakerPaymentSent(id); + + // Now performing the external interaction + IERC20 token = IERC20(tokenAddress); + token.safeTransferFrom(msg.sender, address(this), amount + dexFee); + } + + function takerPaymentApprove( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.TakerApproved; + + emit TakerPaymentApproved(id); + } + + function spendTakerPayment( + bytes32 id, + uint256 amount, + uint256 dexFee, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.TakerApproved, + "Invalid payment state. Must be TakerApproved" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + msg.sender, + taker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress + ) + ); + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.MakerSpent; + + emit TakerPaymentSpent(id, makerSecret); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + payable(dexFeeAddress).transfer(dexFee); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + token.safeTransfer(dexFeeAddress, dexFee); + } + } + + function refundTakerPaymentTimelock( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent || + takerPayments[id].state == TakerPaymentState.TakerApproved, + "Invalid payment state. Must be PaymentSent or TakerApproved" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + if (takerPayments[id].state == TakerPaymentState.TakerApproved) { + require( + block.timestamp >= takerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + } + + if (takerPayments[id].state == TakerPaymentState.PaymentSent) { + require( + block.timestamp >= takerPayments[id].preApproveLockTime, + "Current timestamp didn't exceed payment pre-approve lock time" + ); + } + + takerPayments[id].state = TakerPaymentState.TakerRefunded; + + emit TakerPaymentRefundedTimelock(id); + + uint256 total_amount = amount + dexFee; + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(total_amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, total_amount); + } + } + + function refundTakerPaymentSecret( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.TakerRefunded; + + emit TakerPaymentRefundedSecret(id, takerSecret); + + uint256 total_amount = amount + dexFee; + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(total_amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, total_amount); + } + } + + struct HTLCParams { + bytes32 id; + address taker; + address tokenAddress; + bytes32 takerSecretHash; + bytes32 makerSecretHash; + uint32 paymentLockTime; + } + + function onERC1155Received( + address operator, + address from, + uint256 tokenId, + uint256 value, + bytes calldata data + ) external override returns (bytes4) { + // Decode the data to extract HTLC parameters + HTLCParams memory params = abi.decode(data, (HTLCParams)); + + require( + makerPayments[params.id].state == MakerPaymentState.Uninitialized, + "Maker ERC1155 payment must be Uninitialized" + ); + require(params.taker != address(0), "Taker must not be zero address"); + require( + params.tokenAddress != address(0), + "Token must not be zero address" + ); + require( + msg.sender == params.tokenAddress, + "Token address does not match sender" + ); + require(operator == from, "Operator must be the sender"); + require(value > 0, "Value must be greater than 0"); + require(!isContract(params.taker), "Taker cannot be a contract"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + params.taker, + from, + params.takerSecretHash, + params.makerSecretHash, + params.tokenAddress, + tokenId, + value + ) + ); + + makerPayments[params.id] = MakerPayment( + paymentHash, + params.paymentLockTime, + MakerPaymentState.PaymentSent + ); + emit MakerPaymentSent(params.id); + + // Return this magic value to confirm receipt of ERC1155 token + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, /* operator */ + address, /* from */ + uint256[] calldata, /* ids */ + uint256[] calldata, /* values */ + bytes calldata /* data */ + ) external pure override returns (bytes4) { + revert("Batch transfers not supported"); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC165, IERC165) + returns (bool) + { + return + interfaceId == type(IERC1155Receiver).interfaceId || + interfaceId == type(IERC721Receiver).interfaceId || + super.supportsInterface(interfaceId); + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external override returns (bytes4) { + // Decode the data to extract HTLC parameters + HTLCParams memory params = abi.decode(data, (HTLCParams)); + + require( + makerPayments[params.id].state == MakerPaymentState.Uninitialized, + "Maker ERC721 payment must be Uninitialized" + ); + require(params.taker != address(0), "Taker must not be zero address"); + require( + params.tokenAddress != address(0), + "Token must not be zero address" + ); + require( + msg.sender == params.tokenAddress, + "Token address does not match sender" + ); + require(operator == from, "Operator must be the sender"); + require(!isContract(params.taker), "Taker cannot be a contract"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + params.taker, + from, + params.takerSecretHash, + params.makerSecretHash, + params.tokenAddress, + tokenId + ) + ); + + makerPayments[params.id] = MakerPayment( + paymentHash, + params.paymentLockTime, + MakerPaymentState.PaymentSent + ); + emit MakerPaymentSent(params.id); + + // Return this magic value to confirm receipt of ERC721 token + return this.onERC721Received.selector; + } + + function isContract(address account) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(account) + } + return size > 0; + } +} diff --git a/contracts/EtomicSwapTakerV2.sol b/contracts/EtomicSwapTakerV2.sol new file mode 100644 index 0000000..24e9075 --- /dev/null +++ b/contracts/EtomicSwapTakerV2.sol @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract EtomicSwapTakerV2 { + using SafeERC20 for IERC20; + + enum TakerPaymentState { + Uninitialized, + PaymentSent, + TakerApproved, + MakerSpent, + TakerRefunded + } + + struct TakerPayment { + bytes20 paymentHash; + uint32 preApproveLockTime; + uint32 paymentLockTime; + TakerPaymentState state; + } + + event TakerPaymentSent(bytes32 id); + event TakerPaymentApproved(bytes32 id); + event TakerPaymentSpent(bytes32 id, bytes32 secret); + event TakerPaymentRefundedSecret(bytes32 id, bytes32 secret); + event TakerPaymentRefundedTimelock(bytes32 id); + + mapping(bytes32 => TakerPayment) public takerPayments; + + address public immutable dexFeeAddress; + + constructor(address feeAddress) { + require( + feeAddress != address(0), + "feeAddress must not be zero address" + ); + + dexFeeAddress = feeAddress; + } + + function ethTakerPayment( + bytes32 id, + uint256 dexFee, + address receiver, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 preApproveLockTime, + uint32 paymentLockTime + ) external payable { + require( + takerPayments[id].state == TakerPaymentState.Uninitialized, + "Taker payment is already initialized" + ); + require(receiver != address(0), "Receiver must not be zero address"); + require(msg.value > 0, "ETH value must be greater than zero"); + require(msg.value > dexFee, "ETH value must be greater than dex fee"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.value - dexFee, + dexFee, + receiver, + msg.sender, + takerSecretHash, + makerSecretHash, + address(0) + ) + ); + + takerPayments[id] = TakerPayment( + paymentHash, + preApproveLockTime, + paymentLockTime, + TakerPaymentState.PaymentSent + ); + + emit TakerPaymentSent(id); + } + + function erc20TakerPayment( + bytes32 id, + uint256 amount, + uint256 dexFee, + address tokenAddress, + address receiver, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 preApproveLockTime, + uint32 paymentLockTime + ) external { + require( + takerPayments[id].state == TakerPaymentState.Uninitialized, + "ERC20 v2 payment is already initialized" + ); + require(amount > 0, "Amount must not be zero"); + require(receiver != address(0), "Receiver must not be zero address"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + receiver, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + takerPayments[id] = TakerPayment( + paymentHash, + preApproveLockTime, + paymentLockTime, + TakerPaymentState.PaymentSent + ); + + emit TakerPaymentSent(id); + + // Now performing the external interaction + IERC20 token = IERC20(tokenAddress); + token.safeTransferFrom(msg.sender, address(this), amount + dexFee); + } + + function takerPaymentApprove( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.TakerApproved; + + emit TakerPaymentApproved(id); + } + + function spendTakerPayment( + bytes32 id, + uint256 amount, + uint256 dexFee, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.TakerApproved, + "Invalid payment state. Must be TakerApproved" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + msg.sender, + taker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress + ) + ); + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.MakerSpent; + + emit TakerPaymentSpent(id, makerSecret); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + payable(dexFeeAddress).transfer(dexFee); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + token.safeTransfer(dexFeeAddress, dexFee); + } + } + + function refundTakerPaymentTimelock( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent || + takerPayments[id].state == TakerPaymentState.TakerApproved, + "Invalid payment state. Must be PaymentSent or TakerApproved" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + if (takerPayments[id].state == TakerPaymentState.TakerApproved) { + require( + block.timestamp >= takerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + } + + if (takerPayments[id].state == TakerPaymentState.PaymentSent) { + require( + block.timestamp >= takerPayments[id].preApproveLockTime, + "Current timestamp didn't exceed payment pre-approve lock time" + ); + } + + takerPayments[id].state = TakerPaymentState.TakerRefunded; + + emit TakerPaymentRefundedTimelock(id); + + uint256 total_amount = amount + dexFee; + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(total_amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, total_amount); + } + } + + function refundTakerPaymentSecret( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.TakerRefunded; + + emit TakerPaymentRefundedSecret(id, takerSecret); + + uint256 total_amount = amount + dexFee; + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(total_amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, total_amount); + } + } +} diff --git a/contracts/EtomicSwapV2.sol b/contracts/EtomicSwapV2.sol new file mode 100644 index 0000000..4087dc9 --- /dev/null +++ b/contracts/EtomicSwapV2.sol @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract EtomicSwapV2 { + using SafeERC20 for IERC20; + + enum MakerPaymentState { + Uninitialized, + PaymentSent, + TakerSpent, + MakerRefunded + } + + struct MakerPayment { + bytes20 paymentHash; + uint32 paymentLockTime; + MakerPaymentState state; + } + + event MakerPaymentSent(bytes32 id); + event MakerPaymentSpent(bytes32 id); + event MakerPaymentRefundedTimelock(bytes32 id); + event MakerPaymentRefundedSecret(bytes32 id); + + mapping(bytes32 => MakerPayment) public makerPayments; + + enum TakerPaymentState { + Uninitialized, + PaymentSent, + TakerApproved, + MakerSpent, + TakerRefunded + } + + struct TakerPayment { + bytes20 paymentHash; + uint32 preApproveLockTime; + uint32 paymentLockTime; + TakerPaymentState state; + } + + event TakerPaymentSent(bytes32 id); + event TakerPaymentApproved(bytes32 id); + event TakerPaymentSpent(bytes32 id, bytes32 secret); + event TakerPaymentRefundedSecret(bytes32 id, bytes32 secret); + event TakerPaymentRefundedTimelock(bytes32 id); + + mapping(bytes32 => TakerPayment) public takerPayments; + + address public immutable dexFeeAddress; + + constructor(address feeAddress) { + require(feeAddress != address(0), "feeAddress must not be zero address"); + + dexFeeAddress = feeAddress; + } + + function ethMakerPayment( + bytes32 id, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 paymentLockTime + ) external payable { + require(makerPayments[id].state == MakerPaymentState.Uninitialized, "Maker payment is already initialized"); + require(taker != address(0), "Taker must not be zero address"); + require(msg.value > 0, "ETH value must be greater than zero"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.value, + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + address(0) + ) + ); + + makerPayments[id] = MakerPayment(paymentHash, paymentLockTime, MakerPaymentState.PaymentSent); + + emit MakerPaymentSent(id); + } + + function erc20MakerPayment( + bytes32 id, + uint256 amount, + address tokenAddress, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 paymentLockTime + ) external { + require(makerPayments[id].state == MakerPaymentState.Uninitialized, "Maker payment is already initialized"); + require(amount > 0, "Amount must not be zero"); + require(taker != address(0), "Taker must not be zero address"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + makerPayments[id] = MakerPayment(paymentHash, paymentLockTime, MakerPaymentState.PaymentSent); + + emit MakerPaymentSent(id); + + // Now performing the external interaction + IERC20 token = IERC20(tokenAddress); + token.safeTransferFrom(msg.sender, address(this), amount); + } + + function spendMakerPayment( + bytes32 id, + uint256 amount, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress + ) external { + require(makerPayments[id].state == MakerPaymentState.PaymentSent, "Invalid payment state. Must be PaymentSent"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + msg.sender, + maker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress + ) + ); + require(paymentHash == makerPayments[id].paymentHash, "Invalid paymentHash"); + + makerPayments[id].state = MakerPaymentState.TakerSpent; + + emit MakerPaymentSpent(id); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + } + } + + function refundMakerPaymentTimelock( + bytes32 id, + uint256 amount, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + taker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + require( + block.timestamp >= makerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedTimelock(id); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + } + } + + function refundMakerPaymentSecret( + bytes32 id, + uint256 amount, + address taker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + makerPayments[id].state == MakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + taker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == makerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + makerPayments[id].state = MakerPaymentState.MakerRefunded; + + emit MakerPaymentRefundedSecret(id); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + } + } + + function ethTakerPayment( + bytes32 id, + uint256 dexFee, + address receiver, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 preApproveLockTime, + uint32 paymentLockTime + ) external payable { + require(takerPayments[id].state == TakerPaymentState.Uninitialized, "Taker payment is already initialized"); + require(receiver != address(0), "Receiver must not be zero address"); + require(msg.value > 0, "ETH value must be greater than zero"); + require(msg.value > dexFee, "ETH value must be greater than dex fee"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + msg.value - dexFee, + dexFee, + receiver, + msg.sender, + takerSecretHash, + makerSecretHash, + address(0) + ) + ); + + takerPayments[id] = TakerPayment(paymentHash, preApproveLockTime, paymentLockTime, TakerPaymentState.PaymentSent); + + emit TakerPaymentSent(id); + } + + function erc20TakerPayment( + bytes32 id, + uint256 amount, + uint256 dexFee, + address tokenAddress, + address receiver, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + uint32 preApproveLockTime, + uint32 paymentLockTime + ) external { + require(takerPayments[id].state == TakerPaymentState.Uninitialized, "ERC20 v2 payment is already initialized"); + require(amount > 0, "Amount must not be zero"); + require(dexFee > 0, "Dex fee must not be zero"); + require(receiver != address(0), "Receiver must not be zero address"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + receiver, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + takerPayments[id] = TakerPayment(paymentHash, preApproveLockTime, paymentLockTime, TakerPaymentState.PaymentSent); + + emit TakerPaymentSent(id); + + // Now performing the external interaction + IERC20 token = IERC20(tokenAddress); + token.safeTransferFrom(msg.sender, address(this), amount + dexFee); + } + + function takerPaymentApprove( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.TakerApproved; + + emit TakerPaymentApproved(id); + } + + function spendTakerPayment( + bytes32 id, + uint256 amount, + uint256 dexFee, + address taker, + bytes32 takerSecretHash, + bytes32 makerSecret, + address tokenAddress + ) external { + require(takerPayments[id].state == TakerPaymentState.TakerApproved, "Invalid payment state. Must be TakerApproved"); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + msg.sender, + taker, + takerSecretHash, + sha256(abi.encodePacked(makerSecret)), + tokenAddress + ) + ); + require(paymentHash == takerPayments[id].paymentHash, "Invalid paymentHash"); + + takerPayments[id].state = TakerPaymentState.MakerSpent; + + emit TakerPaymentSpent(id, makerSecret); + + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(amount); + payable(dexFeeAddress).transfer(dexFee); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, amount); + token.safeTransfer(dexFeeAddress, dexFee); + } + } + + function refundTakerPaymentTimelock( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecretHash, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent || takerPayments[id].state == TakerPaymentState.TakerApproved, + "Invalid payment state. Must be PaymentSent or TakerApproved" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + takerSecretHash, + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + if (takerPayments[id].state == TakerPaymentState.TakerApproved) { + require( + block.timestamp >= takerPayments[id].paymentLockTime, + "Current timestamp didn't exceed payment refund lock time" + ); + } + + if (takerPayments[id].state == TakerPaymentState.PaymentSent) { + require( + block.timestamp >= takerPayments[id].preApproveLockTime, + "Current timestamp didn't exceed payment pre-approve lock time" + ); + } + + takerPayments[id].state = TakerPaymentState.TakerRefunded; + + emit TakerPaymentRefundedTimelock(id); + + uint256 total_amount = amount + dexFee; + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(total_amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, total_amount); + } + } + + function refundTakerPaymentSecret( + bytes32 id, + uint256 amount, + uint256 dexFee, + address maker, + bytes32 takerSecret, + bytes32 makerSecretHash, + address tokenAddress + ) external { + require( + takerPayments[id].state == TakerPaymentState.PaymentSent, + "Invalid payment state. Must be PaymentSent" + ); + + bytes20 paymentHash = ripemd160( + abi.encodePacked( + amount, + dexFee, + maker, + msg.sender, + sha256(abi.encodePacked(takerSecret)), + makerSecretHash, + tokenAddress + ) + ); + + require( + paymentHash == takerPayments[id].paymentHash, + "Invalid paymentHash" + ); + + takerPayments[id].state = TakerPaymentState.TakerRefunded; + + emit TakerPaymentRefundedSecret(id, takerSecret); + + uint256 total_amount = amount + dexFee; + if (tokenAddress == address(0)) { + payable(msg.sender).transfer(total_amount); + } else { + IERC20 token = IERC20(tokenAddress); + token.safeTransfer(msg.sender, total_amount); + } + } +} diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol index 17b44d8..5d67bbc 100644 --- a/contracts/Migrations.sol +++ b/contracts/Migrations.sol @@ -1,17 +1,19 @@ -pragma solidity ^0.5.0; -import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/access/Ownable.sol"; contract Migrations is Ownable { - uint public last_completed_migration; + uint256 public lastCompletedMigration; - constructor() public { } + constructor() Ownable(msg.sender) {} - function setCompleted(uint completed) public onlyOwner { - last_completed_migration = completed; + function setCompleted(uint256 completed) public onlyOwner { + lastCompletedMigration = completed; } - function upgrade(address new_address) public onlyOwner { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(last_completed_migration); + function upgrade(address newAddress) public onlyOwner { + Migrations upgraded = Migrations(newAddress); + upgraded.setCompleted(lastCompletedMigration); } -} +} \ No newline at end of file diff --git a/contracts/SwapFeeManager.sol b/contracts/SwapFeeManager.sol new file mode 100644 index 0000000..c4a7ec7 --- /dev/null +++ b/contracts/SwapFeeManager.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.30; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract SwapFeeManager { + using SafeERC20 for IERC20; + + address public immutable dexFeeWallet; + address public immutable burnFeeWallet; + + // address(0) for ETH + event FeesSplit( + address indexed token, + uint256 dexFeeAmount, + uint256 burnFeeAmount + ); + + constructor(address _dexFeeWallet, address _burnFeeWallet) { + require( + _dexFeeWallet != address(0), + "dexFeeWallet must not be zero address" + ); + require( + _burnFeeWallet != address(0), + "burnFeeWallet must not be zero address" + ); + + dexFeeWallet = _dexFeeWallet; + burnFeeWallet = _burnFeeWallet; + } + + // Function to receive Ether + receive() external payable {} + + /** + * @dev Splits the Ether balance in the contract into 75% for dexFeeWallet and 25% for burnFeeWallet. + */ + function splitAndWithdraw() external { + uint256 totalBalance = address(this).balance; + require(totalBalance > 0, "No fees to split"); + + // Calculate 25% of the balance using bit shift + uint256 burnFeeAmount = totalBalance >> 2; + // Calculate the remaining 75% for DEX fee + uint256 dexFeeAmount = totalBalance - burnFeeAmount; + + emit FeesSplit(address(0), dexFeeAmount, burnFeeAmount); + + payable(dexFeeWallet).transfer(dexFeeAmount); + payable(burnFeeWallet).transfer(burnFeeAmount); + } + + /** + * @dev Splits and withdraws ERC20 token balance. + * @param tokenAddress Address of the ERC20 token. + */ + function splitAndWithdrawToken(address tokenAddress) external { + require(tokenAddress != address(0), "Token address must not be zero"); + IERC20 token = IERC20(tokenAddress); + + uint256 totalBalance = token.balanceOf(address(this)); + require(totalBalance > 0, "No token fees to split"); + + // Calculate 25% of the balance using bit shift + uint256 burnFeeAmount = totalBalance >> 2; + // Calculate the remaining 75% for DEX fee + uint256 dexFeeAmount = totalBalance - burnFeeAmount; + + emit FeesSplit(tokenAddress, dexFeeAmount, burnFeeAmount); + + token.safeTransfer(dexFeeWallet, dexFeeAmount); + token.safeTransfer(burnFeeWallet, burnFeeAmount); + } +} diff --git a/contracts/Token.sol b/contracts/Token.sol index 7f8d977..a388d97 100644 --- a/contracts/Token.sol +++ b/contracts/Token.sol @@ -1,7 +1,8 @@ -pragma solidity ^0.5.0; +// SPDX-License-Identifier: MIT -import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +pragma solidity ^0.8.30; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title Standard ERC20 token @@ -15,183 +16,203 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; * compliant implementations may not do it. */ contract Token is IERC20 { - using SafeMath for uint256; - - mapping (address => uint256) private _balances; - - mapping (address => mapping (address => uint256)) private _allowed; - - uint256 private _totalSupply; - - string public constant name = "Just Token"; - - string public constant symbol = "JST"; - - uint8 public constant decimals = 18; - - constructor() public { - _balances[msg.sender] = 1000 ether; - _totalSupply = 1000 ether; - } - - /** - * @dev Total number of tokens in existence - */ - function totalSupply() public view returns (uint256) { - return _totalSupply; - } - - /** - * @dev Gets the balance of the specified address. - * @param owner The address to query the balance of. - * @return An uint256 representing the amount owned by the passed address. - */ - function balanceOf(address owner) public view returns (uint256) { - return _balances[owner]; - } - - /** - * @dev Function to check the amount of tokens that an owner allowed to a spender. - * @param owner address The address which owns the funds. - * @param spender address The address which will spend the funds. - * @return A uint256 specifying the amount of tokens still available for the spender. - */ - function allowance(address owner, address spender) public view returns (uint256) { - return _allowed[owner][spender]; - } - - /** - * @dev Transfer token for a specified address - * @param to The address to transfer to. - * @param value The amount to be transferred. - */ - function transfer(address to, uint256 value) public returns (bool) { - _transfer(msg.sender, to, value); - return true; - } - - /** - * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. - * Beware that changing an allowance with this method brings the risk that someone may use both the old - * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this - * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * @param spender The address which will spend the funds. - * @param value The amount of tokens to be spent. - */ - function approve(address spender, uint256 value) public returns (bool) { - require(spender != address(0)); - - _allowed[msg.sender][spender] = value; - emit Approval(msg.sender, spender, value); - return true; - } - - /** - * @dev Transfer tokens from one address to another. - * Note that while this function emits an Approval event, this is not required as per the specification, - * and other compliant implementations may not emit the event. - * @param from address The address which you want to send tokens from - * @param to address The address which you want to transfer to - * @param value uint256 the amount of tokens to be transferred - */ - function transferFrom(address from, address to, uint256 value) public returns (bool) { - _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); - _transfer(from, to, value); - emit Approval(from, msg.sender, _allowed[from][msg.sender]); - return true; - } - - /** - * @dev Increase the amount of tokens that an owner allowed to a spender. - * approve should be called when allowed_[_spender] == 0. To increment - * allowed value is better to use this function to avoid 2 calls (and wait until - * the first transaction is mined) - * From MonolithDAO Token.sol - * Emits an Approval event. - * @param spender The address which will spend the funds. - * @param addedValue The amount of tokens to increase the allowance by. - */ - function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { - require(spender != address(0)); - - _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); - emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); - return true; - } - - /** - * @dev Decrease the amount of tokens that an owner allowed to a spender. - * approve should be called when allowed_[_spender] == 0. To decrement - * allowed value is better to use this function to avoid 2 calls (and wait until - * the first transaction is mined) - * From MonolithDAO Token.sol - * Emits an Approval event. - * @param spender The address which will spend the funds. - * @param subtractedValue The amount of tokens to decrease the allowance by. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { - require(spender != address(0)); - - _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); - emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); - return true; - } - - /** - * @dev Transfer token for a specified addresses - * @param from The address to transfer from. - * @param to The address to transfer to. - * @param value The amount to be transferred. - */ - function _transfer(address from, address to, uint256 value) internal { - require(to != address(0)); - - _balances[from] = _balances[from].sub(value); - _balances[to] = _balances[to].add(value); - emit Transfer(from, to, value); - } - - /** - * @dev Internal function that mints an amount of the token and assigns it to - * an account. This encapsulates the modification of balances such that the - * proper events are emitted. - * @param account The account that will receive the created tokens. - * @param value The amount that will be created. - */ - function _mint(address account, uint256 value) internal { - require(account != address(0)); - - _totalSupply = _totalSupply.add(value); - _balances[account] = _balances[account].add(value); - emit Transfer(address(0), account, value); - } - - /** - * @dev Internal function that burns an amount of the token of a given - * account. - * @param account The account whose tokens will be burnt. - * @param value The amount that will be burnt. - */ - function _burn(address account, uint256 value) internal { - require(account != address(0)); - - _totalSupply = _totalSupply.sub(value); - _balances[account] = _balances[account].sub(value); - emit Transfer(account, address(0), value); - } - - /** - * @dev Internal function that burns an amount of the token of a given - * account, deducting from the sender's allowance for said account. Uses the - * internal burn function. - * Emits an Approval event (reflecting the reduced allowance). - * @param account The account whose tokens will be burnt. - * @param value The amount that will be burnt. - */ - function _burnFrom(address account, uint256 value) internal { - _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); - _burn(account, value); - emit Approval(account, msg.sender, _allowed[account][msg.sender]); - } + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowed; + + uint256 private _totalSupply; + + string public constant name = "Just Token"; + + string public constant symbol = "JST"; + + uint8 public constant decimals = 18; + + constructor() { + _balances[msg.sender] = 1000 ether; + _totalSupply = 1000 ether; + } + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev Gets the balance of the specified address. + * @param owner The address to query the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address owner) public view returns (uint256) { + return _balances[owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param owner address The address which owns the funds. + * @param spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address owner, address spender) + public + view + returns (uint256) + { + return _allowed[owner][spender]; + } + + /** + * @dev Transfer token for a specified address + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function transfer(address to, uint256 value) public returns (bool) { + _transfer(msg.sender, to, value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param spender The address which will spend the funds. + * @param value The amount of tokens to be spent. + */ + function approve(address spender, uint256 value) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = value; + emit Approval(msg.sender, spender, value); + return true; + } + + /** + * @dev Transfer tokens from one address to another. + * Note that while this function emits an Approval event, this is not required as per the specification, + * and other compliant implementations may not emit the event. + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 the amount of tokens to be transferred + */ + function transferFrom( + address from, + address to, + uint256 value + ) public returns (bool) { + _allowed[from][msg.sender] = _allowed[from][msg.sender] - value; + _transfer(from, to, value); + emit Approval(from, msg.sender, _allowed[from][msg.sender]); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param addedValue The amount of tokens to increase the allowance by. + */ + function increaseAllowance(address spender, uint256 addedValue) + public + returns (bool) + { + require(spender != address(0)); + + _allowed[msg.sender][spender] = + _allowed[msg.sender][spender] + + addedValue; + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) + public + returns (bool) + { + require(spender != address(0)); + + _allowed[msg.sender][spender] = + _allowed[msg.sender][spender] - + subtractedValue; + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Transfer token for a specified addresses + * @param from The address to transfer from. + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function _transfer( + address from, + address to, + uint256 value + ) internal { + require(to != address(0)); + + _balances[from] = _balances[from] - value; + _balances[to] = _balances[to] + value; + emit Transfer(from, to, value); + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param account The account that will receive the created tokens. + * @param value The amount that will be created. + */ + function _mint(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply + value; + _balances[account] = _balances[account] + value; + emit Transfer(address(0), account, value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burn(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply + value; + _balances[account] = _balances[account] - value; + emit Transfer(account, address(0), value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal burn function. + * Emits an Approval event (reflecting the reduced allowance). + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burnFrom(address account, uint256 value) internal { + _allowed[account][msg.sender] = _allowed[account][msg.sender] - value; + _burn(account, value); + emit Approval(account, msg.sender, _allowed[account][msg.sender]); + } } diff --git a/docker-compose.yml b/docker-compose.yml index fc6ede1..2d22927 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,11 @@ version: '2' services: - rpc: - build: - context: ./ - dockerfile: rpc.Dockerfile - ports: - - 8545 - workspace: build: context: ./ dockerfile: workspace.Dockerfile tty: true - env_file: - - .env volumes: - ./:/usr/src/workspace + ports: + - "8545:8545" diff --git a/hardhat.config.js b/hardhat.config.js new file mode 100644 index 0000000..fba57c6 --- /dev/null +++ b/hardhat.config.js @@ -0,0 +1,113 @@ +require("@nomicfoundation/hardhat-ethers"); +require("@nomicfoundation/hardhat-ignition-ethers"); +require("@nomicfoundation/hardhat-verify"); +require("dotenv").config(); + +const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY; +const ETOMIC_SWAP_V1_CREATE2_SALT = process.env.ETOMIC_SWAP_V1_CREATE2_SALT; + +if (!ETOMIC_SWAP_V1_CREATE2_SALT) { + console.warn( + "Warning: Missing ETOMIC_SWAP_V1_CREATE2_SALT. CREATE2 deployments (Ignition --strategy create2) will fail." + ); +} + +const networkConfig = (chainId, rpcEnvVar) => ({ + url: process.env[rpcEnvVar] || "", + accounts: DEPLOYER_PRIVATE_KEY ? [DEPLOYER_PRIVATE_KEY] : [], + chainId, +}); + +module.exports = { + solidity: { + version: "0.8.33", + settings: { + optimizer: { + enabled: true, + runs: 10000, + }, + }, + }, + + ignition: { + strategyConfig: { + create2: { + salt: ETOMIC_SWAP_V1_CREATE2_SALT, + }, + }, + }, + + networks: { + // Local EVM (in-process) + hardhat: { + chainId: 1337, + }, + + // Local JSON-RPC node (e.g., `npx hardhat node`) + localhost: { + url: "http://127.0.0.1:8545", + accounts: DEPLOYER_PRIVATE_KEY ? [DEPLOYER_PRIVATE_KEY] : undefined, + }, + + // ============ TESTNETS (6) ============ + sepolia: networkConfig(11155111, "SEPOLIA_RPC_URL"), + bscTestnet: networkConfig(97, "BSC_TESTNET_RPC_URL"), + polygonMumbai: networkConfig(80001, "POLYGON_MUMBAI_RPC_URL"), + avalancheFujiTestnet: networkConfig(43113, "AVALANCHE_TESTNET_RPC_URL"), + ftmTestnet: networkConfig(4002, "FANTOM_TESTNET_RPC_URL"), + gleecTestnet: networkConfig(11169, "GLEEC_TESTNET_RPC_URL"), + + // ============ MAINNETS (17) ============ + mainnet: networkConfig(1, "MAINNET_RPC_URL"), + bsc: networkConfig(56, "BSC_RPC_URL"), + polygon: networkConfig(137, "POLYGON_RPC_URL"), + avalanche: networkConfig(43114, "AVALANCHE_RPC_URL"), + arbitrumOne: networkConfig(42161, "ARBITRUM_RPC_URL"), + base: networkConfig(8453, "BASE_RPC_URL"), + kcc: networkConfig(321, "KCC_RPC_URL"), + moonriver: networkConfig(1285, "MOONRIVER_RPC_URL"), + moonbeam: networkConfig(1284, "MOONBEAM_RPC_URL"), + opera: networkConfig(250, "FANTOM_RPC_URL"), // Fantom mainnet (named \"opera\" for verification compatibility) + harmony: networkConfig(1666600000, "HARMONY_RPC_URL"), + etc: networkConfig(61, "ETC_RPC_URL"), + rsk: networkConfig(30, "RSK_RPC_URL"), + ewc: networkConfig(246, "EWC_RPC_URL"), + smartbch: networkConfig(10000, "SMARTBCH_RPC_URL"), + ubiq: networkConfig(8, "UBIQ_RPC_URL"), + qtum: networkConfig(3888, "QTUM_RPC_URL"), // Included for deployment only (no explorer verification config yet) + }, + + etherscan: { + // Etherscan V2: single API key works for all V2-supported chains + // (Ethereum, BSC, Polygon, Arbitrum, Base, Avalanche, Optimism, etc.) + apiKey: process.env.ETHERSCAN_API_KEY, + + // For chains NOT in Etherscan V2, add customChains with their own explorer + customChains: [ + { + network: "kcc", + chainId: 321, + urls: { + apiURL: "https://api.explorer.kcc.io/vipapi", + browserURL: "https://explorer.kcc.io", + }, + }, + { + network: "ewc", + chainId: 246, + urls: { + apiURL: "https://explorer.energyweb.org/api", + browserURL: "https://explorer.energyweb.org", + }, + }, + { + network: "gleecTestnet", + chainId: 11169, + urls: { + apiURL: "https://explorer.gleec.dev/api", + browserURL: "https://explorer.gleec.dev", + }, + }, + ], + }, +}; diff --git a/ignition/modules/EtomicSwapV1.js b/ignition/modules/EtomicSwapV1.js new file mode 100644 index 0000000..647b48e --- /dev/null +++ b/ignition/modules/EtomicSwapV1.js @@ -0,0 +1,7 @@ +const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); + +module.exports = buildModule("EtomicSwapV1", (m) => { + const etomicSwap = m.contract("EtomicSwap"); + + return { etomicSwap }; +}); \ No newline at end of file diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js deleted file mode 100644 index 4d5f3f9..0000000 --- a/migrations/1_initial_migration.js +++ /dev/null @@ -1,5 +0,0 @@ -var Migrations = artifacts.require("./Migrations.sol"); - -module.exports = function(deployer) { - deployer.deploy(Migrations); -}; diff --git a/package.json b/package.json index 0a4cab1..bf2421e 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,31 @@ { "name": "ETOMIC_SWAP", - "version": "1.0.0", - "description": "Etomic swap smart contract and helpers", + "version": "1.1.0", + "description": "Etomic swap smart contracts and helpers", "main": "index.js", "scripts": { - "rpc": "node startRPC.js", - "test": "truffle test", - "merge": "node node_modules/sol-merger/bin/sol-merger.js './contracts/*.sol' ./merge" + "compile": "npx hardhat compile", + "test": "npx hardhat test", + "deploy:v1": "npx hardhat ignition deploy ignition/modules/EtomicSwapV1.js --strategy create2 --verify", + "deploy:v1:sepolia": "npx hardhat ignition deploy ignition/modules/EtomicSwapV1.js --network sepolia --strategy create2 --verify", + "deploy:v1:mainnet": "npx hardhat ignition deploy ignition/modules/EtomicSwapV1.js --network mainnet --strategy create2 --verify", + "verify:salt": "node scripts/verify-createx-create2.mjs" }, - "author": "ortgma@gmail.com", "license": "ISC", "dependencies": { - "chai": "^4.1.2", + "@openzeppelin/contracts": "^5.4.0" + }, + "devDependencies": { + "hardhat": "^2.26.0", + "@nomicfoundation/hardhat-ethers": "^3.1.0", + "@nomicfoundation/hardhat-ignition": "^0.15.16", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.16", + "@nomicfoundation/ignition-core": "^0.15.15", + "@nomicfoundation/hardhat-verify": "^2.0.13", + "dotenv": "^16.4.5", + "ethers": "^6.14.0", + "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "ganache-cli": "^6.2.5", - "openzeppelin-solidity": "https://github.com/OpenZeppelin/openzeppelin-solidity.git#release-v2.1.0-solc-0.5", - "request": "^2.83.0", - "request-promise-native": "^1.0.5", - "ripemd160": "^2.0.1", - "sol-merger": "^0.1.0", - "web3": "^1.0.0-beta.27" + "ripemd160": "^2.0.3" } } diff --git a/rpc.Dockerfile b/rpc.Dockerfile deleted file mode 100644 index 91a344f..0000000 --- a/rpc.Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM mhart/alpine-node:11 - -RUN apk update && apk upgrade && apk add git && apk add python && apk add make && apk add g++ - -ADD . /usr/src/rpc - -WORKDIR /usr/src/rpc -RUN yarn - -CMD yarn rpc \ No newline at end of file diff --git a/startRPC.js b/startRPC.js deleted file mode 100644 index 1992e9f..0000000 --- a/startRPC.js +++ /dev/null @@ -1,41 +0,0 @@ -const TestRPC = require("ganache-cli"); - -//create 10 accounts with 1000000 ETH Balance -const server = TestRPC.server({ - accounts: [ - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - { - balance: 0xd3c21bcecceda0000000 - }, - ], - debug: true, - logger: console -}); - -server.listen(8545); diff --git a/test/EtomicSwap.js b/test/EtomicSwap.js index 620c41e..24f87f3 100644 --- a/test/EtomicSwap.js +++ b/test/EtomicSwap.js @@ -1,29 +1,42 @@ -const Swap = artifacts.require('EtomicSwap'); -const Token = artifacts.require('Token'); +const { + expect +} = require("chai"); +const { + ethers +} = require("hardhat"); const crypto = require('crypto'); const RIPEMD160 = require('ripemd160'); - -const EVMThrow = 'VM Exception while processing transaction'; +const { + AbiCoder +} = require("ethers"); require('chai') .use(require('chai-as-promised')) .should(); -function increaseTime (increaseAmount) { - return new Promise((resolve, reject) => { - web3.currentProvider.send({ - jsonrpc: '2.0', - method: 'evm_increaseTime', - id: Date.now(), - params: [increaseAmount] - }, (err, res) => { - return err ? reject(err) : resolve(res); - }); - }); +const INVALID_HASH = 'Invalid paymentHash'; +const INVALID_PAYMENT_STATE = 'Invalid payment state. Must be PaymentSent'; +const INVALID_TIMESTAMP = 'Current timestamp didn\'t exceed payment lock time'; +const UNSUPPORTED_VALUE = 'unsupported addressable value (argument="target", value=null, code=INVALID_ARGUMENT, version=6.16.0)'; + +/** + * Advances the Ethereum Virtual Machine (EVM) time by a specified amount and then mines a new block. + * + * @param {number} increaseAmount The amount of time to advance in seconds. + * + * This function is used in Ethereum smart contract testing to simulate the passage of time. In the EVM, + * time is measured based on block timestamps. The 'evm_increaseTime' method increases the EVM's internal + * clock, but this change only affects the next mined block. Therefore, 'evm_mine' is called immediately + * afterwards to mine a new block, ensuring that the blockchain's timestamp is updated to reflect the time + * change. This approach is essential for testing time-dependent contract features like lock periods or deadlines. + */ +async function advanceTimeAndMine(increaseAmount) { + await ethers.provider.send("evm_increaseTime", [increaseAmount]); + await ethers.provider.send("evm_mine"); } async function currentEvmTime() { - const block = await web3.eth.getBlock("latest"); + const block = await ethers.provider.getBlock("latest"); return block.timestamp; } @@ -34,312 +47,388 @@ const secret = crypto.randomBytes(32); const secretHash = '0x' + new RIPEMD160().update(crypto.createHash('sha256').update(secret).digest()).digest('hex'); const secretHex = '0x' + secret.toString('hex'); +const invalidSecret = crypto.randomBytes(32); +const invalidSecretHex = '0x' + invalidSecret.toString('hex'); + const zeroAddr = '0x0000000000000000000000000000000000000000'; -contract('EtomicSwap', function(accounts) { +describe("EtomicSwap", function() { + + beforeEach(async function() { + accounts = await ethers.getSigners(); - beforeEach(async function () { - this.swap = await Swap.new(); - this.token = await Token.new(); - await this.token.transfer(accounts[1], web3.utils.toWei('100')); + EtomicSwap = await ethers.getContractFactory("EtomicSwap"); + etomicSwap = await EtomicSwap.deploy(); + await etomicSwap.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther('100')); }); - it('should create contract with uninitialized payments', async function () { - const payment = await this.swap.payments(id); - assert.equal(payment[2].valueOf(), PAYMENT_UNINITIALIZED); + it('should create contract with uninitialized payments', async function() { + const payment = await etomicSwap.payments(id); + expect(Number(payment[2])).to.equal(PAYMENT_UNINITIALIZED); }); - it('should allow to send ETH payment', async function () { + it('should allow to send ETH payment', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - accounts[1], + accounts[1].address, secretHash, lockTime ]; - await this.swap.ethPayment(...params, { value: web3.utils.toWei('1') }).should.be.fulfilled; + // Make the ETH payment + await etomicSwap.connect(accounts[0]).ethPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; - const payment = await this.swap.payments(id); + const payment = await etomicSwap.payments(id); - // locktime - assert.equal(payment[1].valueOf(), lockTime); - // status - assert.equal(payment[2].valueOf(), PAYMENT_SENT); + expect(Number(payment[1])).to.equal(lockTime); // locktime + expect(Number(payment[2])).to.equal(PAYMENT_SENT); // status - // should not allow to send again - await this.swap.ethPayment(...params, { value: web3.utils.toWei('1') }).should.be.rejectedWith(EVMThrow); + // Check that it should not allow to send again + await etomicSwap.connect(accounts[0]).ethPayment(...params, { + value: ethers.parseEther('1') + }).should.be.rejectedWith("ETH payment already initialized"); }); - it('should allow to send ERC20 payment', async function () { + it('should allow to send ERC20 payment', async function() { const lockTime = await currentEvmTime() + 1000; + const amount = ethers.parseEther('1'); + const params = [ id, - web3.utils.toWei('1'), - this.token.address, - accounts[1], + amount, + token.target, + accounts[1].address, secretHash, lockTime ]; - await this.token.approve(this.swap.address, web3.utils.toWei('1')); - await this.swap.erc20Payment(...params).should.be.fulfilled; + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + + await token.approve(etomicSwap.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20Payment(...params).should.be.fulfilled; - //check contract token balance - const balance = await this.token.balanceOf(this.swap.address); - assert.equal(balance.toString(), web3.utils.toWei('1')); + // Check contract token balance + const balance = await token.balanceOf(etomicSwap.target); + expect(balance).to.equal(ethers.parseEther('1')); - const payment = await this.swap.payments(id); + const payment = await etomicSwap.payments(id); - // locktime - assert.equal(payment[1].valueOf(), lockTime); - // status - assert.equal(payment[2].valueOf(), PAYMENT_SENT); + // Check locktime and status + expect(payment[1]).to.equal(BigInt(lockTime)); + expect(payment[2]).to.equal(BigInt(PAYMENT_SENT)); - // should not allow to deposit again - await this.swap.erc20Payment(...params).should.be.rejectedWith(EVMThrow); + // Should not allow to deposit again + await etomicSwapRunner0.erc20Payment(...params).should.be.rejectedWith("ERC20 payment already initialized"); }); - it('should allow sender to refund ETH payment after locktime', async function () { + it('should allow sender to refund ETH payment after locktime', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - accounts[1], + accounts[1].address, secretHash, lockTime ]; - // not allow to refund if payment was not sent - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, zeroAddr, accounts[1]).should.be.rejectedWith(EVMThrow); + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwap.connect(accounts[1]); - await this.swap.ethPayment(...params, { value: web3.utils.toWei('1') }).should.be.fulfilled; + // Not allow to refund if payment was not sent + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, zeroAddr, accounts[1].address) + .should.be.rejectedWith(INVALID_PAYMENT_STATE); - // not allow to refund before locktime - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, zeroAddr, accounts[1]).should.be.rejectedWith(EVMThrow); + // Make the ETH payment + await etomicSwapRunner0.ethPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; - await increaseTime(1000); + // Not allow to refund before locktime + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, zeroAddr, accounts[1].address).should.be.rejectedWith(INVALID_TIMESTAMP); - // not allow to call refund from non-sender address - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, zeroAddr, accounts[1], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); - // not allow to refund invalid amount - await this.swap.senderRefund(id, web3.utils.toWei('2'), secretHash, zeroAddr, accounts[1]).should.be.rejectedWith(EVMThrow); + // Not allow to call refund from non-sender address + await etomicSwapRunner1.senderRefund(id, ethers.parseEther('1'), secretHash, zeroAddr, accounts[1].address).should.be.rejectedWith(INVALID_HASH); - // success refund - const balanceBefore = web3.utils.toBN(await web3.eth.getBalance(accounts[0])); - const gasPrice = web3.utils.toWei('100', 'gwei'); + // Not allow to refund invalid amount + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('2'), secretHash, zeroAddr, accounts[1].address).should.be.rejectedWith(INVALID_HASH); - const tx = await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, zeroAddr, accounts[1], { gasPrice }).should.be.fulfilled; - const balanceAfter = web3.utils.toBN(await web3.eth.getBalance(accounts[0])); + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); - const txFee = web3.utils.toBN(gasPrice).mul(web3.utils.toBN(tx.receipt.gasUsed)); - // check sender balance - assert.equal(balanceAfter.sub(balanceBefore).add(txFee).toString(), web3.utils.toWei('1')); + const tx = await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, zeroAddr, accounts[1].address, { + gasPrice + }).should.be.fulfilled; - const payment = await this.swap.payments(id); - assert.equal(payment[2].valueOf(), SENDER_REFUNDED); + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; - // not allow to refund again - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, zeroAddr, accounts[1]).should.be.rejectedWith(EVMThrow); + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwap.payments(id); + expect(payment.state).to.equal(BigInt(SENDER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, zeroAddr, accounts[1].address).should.be.rejectedWith(INVALID_PAYMENT_STATE); }); - it('should allow sender to refund ERC20 payment after locktime', async function () { + it('should allow sender to refund ERC20 payment after locktime', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - web3.utils.toWei('1'), - this.token.address, - accounts[1], + ethers.parseEther('1'), + token.target, + accounts[1].address, secretHash, lockTime ]; - await this.token.approve(this.swap.address, web3.utils.toWei('1')); - await this.swap.erc20Payment(...params).should.be.fulfilled; + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwap.connect(accounts[1]); - // not allow to refund if payment was not sent - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, this.token.address, accounts[1]).should.be.rejectedWith(EVMThrow); + await token.approve(etomicSwap.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20Payment(...params)).to.be.fulfilled; - // not allow to refund before locktime - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, this.token.address, accounts[1]).should.be.rejectedWith(EVMThrow); + // Not allow to refund before locktime + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, token.target, accounts[1].address).should.be.rejectedWith(INVALID_TIMESTAMP); - await increaseTime(1000); + await advanceTimeAndMine(1000); - // not allow to call refund from non-sender address - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, this.token.address, accounts[1], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); + // Not allow to call refund from non-sender address + await etomicSwapRunner1.senderRefund(id, ethers.parseEther('1'), secretHash, token.target, accounts[1].address) + .should.be.rejectedWith(INVALID_HASH); - // not allow to refund invalid amount - await this.swap.senderRefund(id, web3.utils.toWei('2'), secretHash, this.token.address, accounts[1]).should.be.rejectedWith(EVMThrow); + // Not allow to refund invalid amount + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('2'), secretHash, token.target, accounts[1].address).should.be.rejectedWith(INVALID_HASH); - // success refund - const balanceBefore = web3.utils.toBN(await this.token.balanceOf(accounts[0])); + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, this.token.address, accounts[1]).should.be.fulfilled; + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, token.target, accounts[1].address).should.be.fulfilled; - const balanceAfter = web3.utils.toBN(await this.token.balanceOf(accounts[0])); + const balanceAfter = await token.balanceOf(accounts[0].address); - // check sender balance - assert.equal(balanceAfter.sub(balanceBefore).toString(), web3.utils.toWei('1')); + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); - const payment = await this.swap.payments(id); - assert.equal(payment[2].valueOf(), SENDER_REFUNDED); + // Check the state of the payment + const payment = await etomicSwap.payments(id); + expect(payment.state).to.equal(BigInt(SENDER_REFUNDED)); - // not allow to refund again - await this.swap.senderRefund(id, web3.utils.toWei('1'), secretHash, this.token.address, accounts[1]).should.be.rejectedWith(EVMThrow); + // Do not allow to refund again + await etomicSwapRunner0.senderRefund(id, ethers.parseEther('1'), secretHash, token.target, accounts[1].address).should.be.rejectedWith(INVALID_PAYMENT_STATE); }); - it('should allow receiver to spend ETH payment by revealing a secret', async function () { + it('should allow receiver to spend ETH payment by revealing a secret', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - accounts[1], + accounts[1].address, secretHash, lockTime ]; - // should not allow to spend uninitialized payment - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, zeroAddr, accounts[0], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwap.connect(accounts[1]); + + // Should not allow to spend uninitialized payment + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, zeroAddr, accounts[0].address).should.be.rejectedWith(INVALID_PAYMENT_STATE); + + // Make the ETH payment + await etomicSwapRunner0.ethPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; - await this.swap.ethPayment(...params, { value: web3.utils.toWei('1') }).should.be.fulfilled; + // Should not allow to spend with invalid secret + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), invalidSecretHex, zeroAddr, accounts[0].address).should.be.rejectedWith(INVALID_HASH); - // should not allow to spend with invalid secret - await this.swap.receiverSpend(id, web3.utils.toWei('1'), id, zeroAddr, accounts[0], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); - // should not allow to spend invalid amount - await this.swap.receiverSpend(id, web3.utils.toWei('2'), secretHex, zeroAddr, accounts[0], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); + // Should not allow to spend invalid amount + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('2'), secretHex, zeroAddr, accounts[0].address).should.be.rejectedWith(INVALID_HASH); - // should not allow to claim from non-receiver address even with valid secret - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, zeroAddr, accounts[0], { from: accounts[0] }).should.be.rejectedWith(EVMThrow); + // Should not allow to claim from non-receiver address even with valid secret + await etomicSwapRunner0.receiverSpend(id, ethers.parseEther('1'), secretHex, zeroAddr, accounts[0].address).should.be.rejectedWith(INVALID_HASH); - // success spend - const balanceBefore = web3.utils.toBN(await web3.eth.getBalance(accounts[1])); - const gasPrice = web3.utils.toWei('100', 'gwei'); + // Success spend + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); - const tx = await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, zeroAddr, accounts[0], { from: accounts[1], gasPrice }).should.be.fulfilled; - const txFee = web3.utils.toBN(gasPrice).mul(web3.utils.toBN(tx.receipt.gasUsed)); + const gasPrice = ethers.parseUnits('100', 'gwei'); - const balanceAfter = web3.utils.toBN(await web3.eth.getBalance(accounts[1])); + const tx = await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, zeroAddr, accounts[0].address, { + gasPrice + }).should.be.fulfilled; - // check receiver balance - assert.equal(balanceAfter.sub(balanceBefore).add(txFee).toString(), web3.utils.toWei('1')); + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasPrice * gasUsed; - const payment = await this.swap.payments(id); + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check receiver balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); - // status - assert.equal(payment[2].valueOf(), RECEIVER_SPENT); + // Check the state of the payment + const payment = await etomicSwap.payments(id); + expect(payment.state).to.equal(BigInt(RECEIVER_SPENT)); - // should not allow to spend again - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, zeroAddr, accounts[0], { from: accounts[1], gasPrice }).should.be.rejectedWith(EVMThrow); + // Should not allow to spend again + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, zeroAddr, accounts[0].address, { + gasPrice + }).should.be.rejectedWith(INVALID_PAYMENT_STATE); }); - it('should allow receiver to spend ERC20 payment by revealing a secret', async function () { + it('should allow receiver to spend ERC20 payment by revealing a secret', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - web3.utils.toWei('1'), - this.token.address, - accounts[1], + ethers.parseEther('1'), + token.target, + accounts[1].address, secretHash, lockTime ]; - // should not allow to spend uninitialized payment - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, this.token.address, accounts[0], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwap.connect(accounts[1]); - await this.token.approve(this.swap.address, web3.utils.toWei('1')); - await this.swap.erc20Payment(...params).should.be.fulfilled; + // Should not allow to spend uninitialized payment + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, token.address, accounts[0].address).should.be.rejectedWith(UNSUPPORTED_VALUE); - // should not allow to spend with invalid secret - await this.swap.receiverSpend(id, web3.utils.toWei('1'), id, this.token.address, accounts[0], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); - // should not allow to spend invalid amount - await this.swap.receiverSpend(id, web3.utils.toWei('2'), secretHex, this.token.address, accounts[0], { from: accounts[1] }).should.be.rejectedWith(EVMThrow); + await token.approve(etomicSwap.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20Payment(...params).should.be.fulfilled; - // should not allow to claim from non-receiver address even with valid secret - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, this.token.address, accounts[0], { from: accounts[0] }).should.be.rejectedWith(EVMThrow); + // Should not allow to spend with invalid secret + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), invalidSecretHex, token.target, accounts[0].address).should.be.rejectedWith(INVALID_HASH); - // success spend - const balanceBefore = web3.utils.toBN(await this.token.balanceOf(accounts[1])); + // Should not allow to spend invalid amount + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('2'), secretHex, token.target, accounts[0].address).should.be.rejectedWith(INVALID_HASH); - const gasPrice = web3.utils.toWei('100', 'gwei'); - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, this.token.address, accounts[0], { from: accounts[1], gasPrice }).should.be.fulfilled; - const balanceAfter = web3.utils.toBN(await this.token.balanceOf(accounts[1])); + // Should not allow to claim from non-receiver address even with valid secret + await etomicSwapRunner0.receiverSpend(id, ethers.parseEther('1'), secretHex, token.target, accounts[0].address).should.be.rejectedWith(INVALID_HASH); - // check receiver balance - assert.equal(balanceAfter.sub(balanceBefore).toString(), web3.utils.toWei('1')); + // Success spend + const balanceBefore = await token.balanceOf(accounts[1]); - const payment = await this.swap.payments(id); + const gasPrice = ethers.parseUnits('100', 'gwei'); - // status - assert.equal(payment[2].valueOf(), RECEIVER_SPENT); + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, token.target, accounts[0].address, { + gasPrice + }).should.be.fulfilled; - // should not allow to spend again - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, this.token.address, accounts[0], { from: accounts[1], gasPrice }).should.be.rejectedWith(EVMThrow); + const balanceAfter = await token.balanceOf(accounts[1].address); + // Check receiver balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwap.payments(id); + expect(payment.state).to.equal(BigInt(RECEIVER_SPENT)); + + // Should not allow to spend again + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, token.target, accounts[0].address, { + gasPrice + }).should.be.rejectedWith(INVALID_PAYMENT_STATE); }); - it('should allow receiver to spend ETH payment by revealing a secret even after locktime', async function () { + it('should allow receiver to spend ETH payment by revealing a secret even after locktime', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - accounts[1], + accounts[1].address, secretHash, lockTime ]; - await this.swap.ethPayment(...params, { value: web3.utils.toWei('1') }).should.be.fulfilled; + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwap.connect(accounts[1]); - await increaseTime(1000); + // Make the ETH payment + await etomicSwapRunner0.ethPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; - // success spend - const balanceBefore = web3.utils.toBN(await web3.eth.getBalance(accounts[1])); - const gasPrice = web3.utils.toWei('100', 'gwei'); + await advanceTimeAndMine(1000); - const tx = await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, zeroAddr, accounts[0], { from: accounts[1], gasPrice }).should.be.fulfilled; - const txFee = web3.utils.toBN(gasPrice).mul(web3.utils.toBN(tx.receipt.gasUsed)); + // Success spend + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); - const balanceAfter = web3.utils.toBN(await web3.eth.getBalance(accounts[1])); + const gasPrice = ethers.parseUnits('100', 'gwei'); - // check receiver balance - assert.equal(balanceAfter.sub(balanceBefore).add(txFee).toString(), web3.utils.toWei('1')); + const tx = await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, zeroAddr, accounts[0].address, { + gasPrice + }).should.be.fulfilled; - const payment = await this.swap.payments(id); + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasPrice * gasUsed; - // status - assert.equal(payment[2].valueOf(), RECEIVER_SPENT); + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check receiver balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); - // should not allow to spend again - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, zeroAddr, accounts[0], { from: accounts[1], gasPrice }).should.be.rejectedWith(EVMThrow); + const payment = await etomicSwap.payments(id); + expect(payment.state).to.equal(BigInt(RECEIVER_SPENT)); + + // Should not allow to spend again + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, zeroAddr, accounts[0].address, { + gasPrice + }).should.be.rejectedWith(INVALID_PAYMENT_STATE); }); - it('should allow receiver to spend ERC20 payment by revealing a secret', async function () { + it('should allow receiver to spend ERC20 payment by revealing a secret even after locktime', async function() { const lockTime = await currentEvmTime() + 1000; const params = [ id, - web3.utils.toWei('1'), - this.token.address, - accounts[1], + ethers.parseEther('1'), + token.target, + accounts[1].address, secretHash, lockTime ]; - await this.token.approve(this.swap.address, web3.utils.toWei('1')); - await this.swap.erc20Payment(...params).should.be.fulfilled; + let etomicSwapRunner0 = etomicSwap.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwap.connect(accounts[1]); - await increaseTime(1000); + await token.approve(etomicSwap.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20Payment(...params)).to.be.fulfilled; - // success spend - const balanceBefore = web3.utils.toBN(await this.token.balanceOf(accounts[1])); + await advanceTimeAndMine(1000); - const gasPrice = web3.utils.toWei('100', 'gwei'); - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, this.token.address, accounts[0], { from: accounts[1], gasPrice }).should.be.fulfilled; - const balanceAfter = web3.utils.toBN(await this.token.balanceOf(accounts[1])); + // Success spend + const balanceBefore = await token.balanceOf(accounts[1].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); - // check receiver balance - assert.equal(balanceAfter.sub(balanceBefore).toString(), web3.utils.toWei('1')); + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, token.target, accounts[0].address, { + gasPrice + }).should.be.fulfilled; - const payment = await this.swap.payments(id); + const balanceAfter = await token.balanceOf(accounts[1]); + // Check receiver balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); - // status - assert.equal(payment[2].valueOf(), RECEIVER_SPENT); + // Check the state of the payment + const payment = await etomicSwap.payments(id); + expect(payment.state).to.equal(BigInt(RECEIVER_SPENT)); - // should not allow to spend again - await this.swap.receiverSpend(id, web3.utils.toWei('1'), secretHex, this.token.address, accounts[0], { from: accounts[1], gasPrice }).should.be.rejectedWith(EVMThrow); + // Should not allow to spend again + await etomicSwapRunner1.receiverSpend(id, ethers.parseEther('1'), secretHex, token.target, accounts[0].address, { + gasPrice + }).should.be.rejectedWith(INVALID_PAYMENT_STATE); }); -}); + +}); \ No newline at end of file diff --git a/test/EtomicSwapMakerNftV2Test.js b/test/EtomicSwapMakerNftV2Test.js new file mode 100644 index 0000000..b0512a2 --- /dev/null +++ b/test/EtomicSwapMakerNftV2Test.js @@ -0,0 +1,487 @@ +const { + expect +} = require("chai"); +const { + ethers +} = require("hardhat"); +const crypto = require('crypto'); +const {AbiCoder} = require("ethers"); + +require('chai') + .use(require('chai-as-promised')) + .should(); + +const INVALID_HASH = 'Invalid paymentHash'; +const INVALID_PAYMENT_STATE_SENT = 'Invalid payment state. Must be PaymentSent'; +const REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment refund lock time'; + +/** + * Advances the Ethereum Virtual Machine (EVM) time by a specified amount and then mines a new block. + * + * @param {number} increaseAmount The amount of time to advance in seconds. + * + * This function is used in Ethereum smart contract testing to simulate the passage of time. In the EVM, + * time is measured based on block timestamps. The 'evm_increaseTime' method increases the EVM's internal + * clock, but this change only affects the next mined block. Therefore, 'evm_mine' is called immediately + * afterwards to mine a new block, ensuring that the blockchain's timestamp is updated to reflect the time + * change. This approach is essential for testing time-dependent contract features like lock periods or deadlines. + */ +async function advanceTimeAndMine(increaseAmount) { + await ethers.provider.send("evm_increaseTime", [increaseAmount]); + await ethers.provider.send("evm_mine"); +} + +async function currentEvmTime() { + const block = await ethers.provider.getBlock("latest"); + return block.timestamp; +} + +const id = '0x' + crypto.randomBytes(32).toString('hex'); +const [MAKER_PAYMENT_UNINITIALIZED, MAKER_PAYMENT_SENT, TAKER_SPENT, MAKER_REFUNDED] = [0, 1, 2, 3]; + +const takerSecret = crypto.randomBytes(32); +const takerSecretHash = '0x' + crypto.createHash('sha256').update(takerSecret).digest('hex'); + +const makerSecret = crypto.randomBytes(32); +const makerSecretHash = '0x' + crypto.createHash('sha256').update(makerSecret).digest('hex'); + +const invalidSecret = crypto.randomBytes(32); + +describe("EtomicSwapMakerNftV2", function() { + + beforeEach(async function() { + accounts = await ethers.getSigners(); + + EtomicSwapMakerNftV2 = await ethers.getContractFactory("EtomicSwapMakerNftV2"); + etomicSwapMakerNftV2 = await EtomicSwapMakerNftV2.deploy(); + await etomicSwapMakerNftV2.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + Erc721Token = await ethers.getContractFactory("Erc721Token"); + erc721token = await Erc721Token.deploy("MyNFT", "MNFT"); + await erc721token.waitForDeployment(); + + Erc1155Token = await ethers.getContractFactory("Erc1155Token"); + erc1155token = await Erc1155Token.deploy("uri"); + await erc1155token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther('100')); + }); + + it('should create contract with uninitialized payments', async function() { + const makerPayment = await etomicSwapMakerNftV2.makerPayments(id); + expect(Number(makerPayment[2])).to.equal(MAKER_PAYMENT_UNINITIALIZED); + }); + + it('should allow maker to send ERC721 payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; // Assuming token ID 1 is minted to accounts[0] in Erc721Token contract + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + const makerErc721Runner0 = erc721token.connect(accounts[0]); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + // Explicitly specify the method signature. + const tx = await makerErc721Runner0['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapMakerNftV2.target, tokenId, data).should.be.fulfilled; + const receipt = await tx.wait(); + console.log(`Gas Used: ${receipt.gasUsed.toString()}`); + + // Check the payment lockTime and state + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(Number(payment[1])).to.equal(paymentLockTime); + expect(Number(payment[2])).to.equal(MAKER_PAYMENT_SENT); + + // Should not allow to send again ( reverted with custom error ERC721InsufficientApproval ) + await expect(makerErc721Runner0['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapMakerNftV2.target, tokenId, data)).to.be.rejectedWith("ERC721InsufficientApproval"); + }); + + it('should allow maker to send ERC1155 payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 2; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + const makerErc1155Runner0 = erc1155token.connect(accounts[0]); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + const tx = await makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, amountToSend, data).should.be.fulfilled; + const receipt = await tx.wait(); + console.log(`Gas Used: ${receipt.gasUsed.toString()}`); + + // Check the payment lockTime and state + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(Number(payment[1])).to.equal(paymentLockTime); + expect(Number(payment[2])).to.equal(MAKER_PAYMENT_SENT); + + // Check the balance of the token in the swap contract + const tokenBalance = await erc1155token.balanceOf(etomicSwapMakerNftV2.target, tokenId); + expect(tokenBalance).to.equal(BigInt(amountToSend)); + + // Check sending same params again - should fail + await expect(makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, amountToSend, data)).to.be.rejectedWith("ERC1155InsufficientBalance"); + + // Maker should be capable to send more tokens, if they have it. Note: Check Erc1155.sol file. By default, ERC1155 is minted with 3 value. + const id1 = '0x' + crypto.randomBytes(32).toString('hex'); + const data1 = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id1, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + await makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, 1, data1).should.be.fulfilled; + + // Check sending more tokens than the sender owns - should fail + const id2 = '0x' + crypto.randomBytes(32).toString('hex'); + const data2 = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id2, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + await expect(makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, amountToSend, data2)).to.be.rejectedWith("ERC1155InsufficientBalance"); + }); + + it('should allow taker to spend ERC721 maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + // Explicitly specify the method signature. + await erc721token.connect(accounts[0])['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapMakerNftV2.target, tokenId, data).should.be.fulfilled; + + // Check the ownership of the token before Taker spend Maker ERC721 payment - should be owned by Swap NFT contract + const tokenOwnerBeforeTakerSpend = await erc721token.ownerOf(tokenId); + expect(tokenOwnerBeforeTakerSpend).to.equal(etomicSwapMakerNftV2.target); + + const takerSwapRunner = etomicSwapMakerNftV2.connect(accounts[1]); + + const spendParamsInvalidSecret = [ + id, + accounts[0].address, + takerSecretHash, + invalidSecret, + erc721token.target, + tokenId + ]; + // Attempt to spend with invalid secret - should fail + await takerSwapRunner.spendErc721MakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + accounts[0].address, + takerSecretHash, + makerSecret, + erc721token.target, + tokenId + ]; + + // should not allow to spend from non-taker address even with valid secret + await etomicSwapMakerNftV2.connect(accounts[2]).spendErc721MakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // Successful spend by Taker with valid secret + await takerSwapRunner.spendErc721MakerPayment(...spendParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // Check the ownership of the token - should be transferred to the Taker (accounts[1]) + const tokenOwner = await erc721token.ownerOf(tokenId); + expect(tokenOwner).to.equal(accounts[1].address); + + // should not allow to spend again + await takerSwapRunner.spendErc721MakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }) + + it('should allow taker to spend ERC1155 maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 2; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + await erc1155token.connect(accounts[0]).safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, amountToSend, data).should.be.fulfilled; + + // Check the balance of the token before Taker spend Maker ERC1155 payment - should be in Swap NFT contract + let tokenBalanceBeforeTakerSpend = await erc1155token.balanceOf(etomicSwapMakerNftV2.target, tokenId); + expect(tokenBalanceBeforeTakerSpend).to.equal(BigInt(amountToSend)); + + const takerSwapRunner = etomicSwapMakerNftV2.connect(accounts[1]); + + const spendParamsInvalidSecret = [ + id, + accounts[0].address, + takerSecretHash, + invalidSecret, + erc1155token.target, + tokenId, + amountToSend + ]; + // Attempt to spend with invalid secret - should fail + await takerSwapRunner.spendErc1155MakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + accounts[0].address, + takerSecretHash, + makerSecret, + erc1155token.target, + tokenId, + amountToSend + ]; + + // should not allow to spend from non-taker address even with valid secret + await etomicSwapMakerNftV2.connect(accounts[2]).spendErc1155MakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // Successful spend by Taker with valid secret + await takerSwapRunner.spendErc1155MakerPayment(...spendParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // Check the balance of the token - should be transferred to the Taker (accounts[1]) + let tokenBalance = await erc1155token.balanceOf(accounts[1].address, tokenId); + expect(tokenBalance).to.equal(BigInt(amountToSend)); + + // Check that the Swap NFT contract no longer holds the tokens + tokenBalance = await erc1155token.balanceOf(etomicSwapMakerNftV2.target, tokenId); + expect(tokenBalance).to.equal(BigInt(0)); + + // should not allow to spend again + await takerSwapRunner.spendErc1155MakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC721 payment after locktime', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapMakerNftV2.connect(accounts[0]); + + // Not allow maker to refund if payment was not sent + const refundParams = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + erc721token.target, + tokenId + ]; + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + // Explicitly specify the method signature. + await erc721token.connect(accounts[0])['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapMakerNftV2.target, tokenId, data).should.be.fulfilled; + + // Not allow to refund before locktime + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-maker address + await etomicSwapMakerNftV2.connect(accounts[1]).refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Successful refund by maker after locktime + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow maker to refund again + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC1155 payment after locktime', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 3; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapMakerNftV2.connect(accounts[0]); + + const refundParams = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + erc1155token.target, + tokenId, + amountToSend + ]; + + // Not allow maker to refund if payment was not sent + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + await erc1155token.connect(accounts[0]).safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, amountToSend, data).should.be.fulfilled; + + // Not allow to refund before locktime + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-maker address + await etomicSwapMakerNftV2.connect(accounts[1]).refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH) + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + erc1155token.target, + tokenId, + 2 + ]; + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Successful refund by maker after locktime + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Check the balance of the token - should be back to the maker (accounts[0]) + const tokenBalance = await erc1155token.balanceOf(accounts[0].address, tokenId); + expect(tokenBalance).to.equal(BigInt(amountToSend)); + + // Do not allow to refund again + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC721 payment using taker secret', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapMakerNftV2.connect(accounts[0]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + accounts[1].address, + takerSecret, + makerSecretHash, + erc721token.target, + tokenId + ]; + await makerSwapRunner.refundErc721MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + // Explicitly specify the method signature. + await erc721token.connect(accounts[0])['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapMakerNftV2.target, tokenId, data).should.be.fulfilled; + + // Not allow to call refund from non-maker address + await etomicSwapMakerNftV2.connect(accounts[1]).refundErc721MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Successful refund by maker using taker secret + await makerSwapRunner.refundErc721MakerPaymentSecret(...refundParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow maker to refund again + await makerSwapRunner.refundErc721MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC1155 payment using taker secret', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 3; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapMakerNftV2.connect(accounts[0]); + + const refundParams = [ + id, + accounts[1].address, + takerSecret, + makerSecretHash, + erc1155token.target, + tokenId, + amountToSend + ]; + + // Not allow maker to refund if payment was not sent + await makerSwapRunner.refundErc1155MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the etomicSwapMakerNftV2 contract. + await erc1155token.connect(accounts[0]).safeTransferFrom(accounts[0].address, etomicSwapMakerNftV2.target, tokenId, amountToSend, data).should.be.fulfilled; + + // Not allow to call refund from non-maker address + await etomicSwapMakerNftV2.connect(accounts[1]).refundErc1155MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + accounts[1].address, + takerSecret, + makerSecretHash, + erc1155token.target, + tokenId, + 2 + ]; + await makerSwapRunner.refundErc1155MakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + await makerSwapRunner.refundErc1155MakerPaymentSecret(...refundParams).should.be.fulfilled; + + // Successful refund by maker using taker secret + const payment = await etomicSwapMakerNftV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Do not allow to refund again + await makerSwapRunner.refundErc1155MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + +}); + diff --git a/test/EtomicSwapMakerV2Test.js b/test/EtomicSwapMakerV2Test.js new file mode 100644 index 0000000..5ab0da9 --- /dev/null +++ b/test/EtomicSwapMakerV2Test.js @@ -0,0 +1,551 @@ +const { + expect +} = require("chai"); +const { + ethers +} = require("hardhat"); +const crypto = require('crypto'); + +require('chai') + .use(require('chai-as-promised')) + .should(); + +const INVALID_HASH = 'Invalid paymentHash'; +const INVALID_PAYMENT_STATE_SENT = 'Invalid payment state. Must be PaymentSent'; +const REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment refund lock time'; + +/** + * Advances the Ethereum Virtual Machine (EVM) time by a specified amount and then mines a new block. + * + * @param {number} increaseAmount The amount of time to advance in seconds. + * + * This function is used in Ethereum smart contract testing to simulate the passage of time. In the EVM, + * time is measured based on block timestamps. The 'evm_increaseTime' method increases the EVM's internal + * clock, but this change only affects the next mined block. Therefore, 'evm_mine' is called immediately + * afterwards to mine a new block, ensuring that the blockchain's timestamp is updated to reflect the time + * change. This approach is essential for testing time-dependent contract features like lock periods or deadlines. + */ +async function advanceTimeAndMine(increaseAmount) { + await ethers.provider.send("evm_increaseTime", [increaseAmount]); + await ethers.provider.send("evm_mine"); +} + +async function currentEvmTime() { + const block = await ethers.provider.getBlock("latest"); + return block.timestamp; +} + +const id = '0x' + crypto.randomBytes(32).toString('hex'); +const [MAKER_PAYMENT_UNINITIALIZED, MAKER_PAYMENT_SENT, TAKER_SPENT, MAKER_REFUNDED] = [0, 1, 2, 3]; + +const takerSecret = crypto.randomBytes(32); +const takerSecretHash = '0x' + crypto.createHash('sha256').update(takerSecret).digest('hex'); + +const makerSecret = crypto.randomBytes(32); +const makerSecretHash = '0x' + crypto.createHash('sha256').update(makerSecret).digest('hex'); + +const invalidSecret = crypto.randomBytes(32); + +const zeroAddr = '0x0000000000000000000000000000000000000000'; + +describe("EtomicSwapMakerV2", function() { + + beforeEach(async function() { + accounts = await ethers.getSigners(); + + EtomicSwapMakerV2 = await ethers.getContractFactory("EtomicSwapMakerV2"); + etomicSwapMakerV2 = await EtomicSwapMakerV2.deploy(); + await etomicSwapMakerV2.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther('100')); + }); + + it('should create contract with uninitialized maker payments', async function() { + const makerPayment = await etomicSwapMakerV2.makerPayments(id); + expect(Number(makerPayment[2])).to.equal(MAKER_PAYMENT_UNINITIALIZED); + }); + + it('should allow maker to send ETH payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + paymentLockTime + ]; + + // Make the ETH payment + await etomicSwapMakerV2.connect(accounts[0]).ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const payment = await etomicSwapMakerV2.makerPayments(id); + + expect(Number(payment[1])).to.equal(paymentLockTime); + expect(Number(payment[2])).to.equal(MAKER_PAYMENT_SENT); + + // Check that it should not allow to send again + await etomicSwapMakerV2.connect(accounts[0]).ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.rejectedWith("Maker payment is already initialized"); + }); + + it('should allow maker to send ERC20 payment', async function() { + const currentTime = await currentEvmTime(); + + const paymentLockTime = currentTime + 100; + + const payment_params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapMakerV2.connect(accounts[0]); + + await token.approve(etomicSwapMakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20MakerPayment(...payment_params).should.be.fulfilled; + + // Check contract token balance + const balance = await token.balanceOf(etomicSwapMakerV2.target); + expect(balance).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapMakerV2.makerPayments(id); + + // Check locktime and status + expect(payment[1]).to.equal(BigInt(paymentLockTime)); + expect(payment[2]).to.equal(BigInt(MAKER_PAYMENT_SENT)); + + // Should not allow to send payment again + await etomicSwapRunner0.erc20MakerPayment(...payment_params).should.be.rejectedWith("Maker payment is already initialized"); + }); + + it('should allow taker to spend ETH maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + paymentLockTime + ]; + + const makerSwapRunner = etomicSwapMakerV2.connect(accounts[0]); + const takerSwapRunner = etomicSwapMakerV2.connect(accounts[1]); + + // Make the ETH payment + await makerSwapRunner.ethMakerPayment(...payment_params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const spendParamsInvalidSecret = [ + id, + ethers.parseEther('1'), + accounts[0].address, + takerSecretHash, + invalidSecret, + zeroAddr, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParamsInvalidAmount = [ + id, + ethers.parseEther('0.9'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidAmount).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + ethers.parseEther('1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + // should not allow to spend from non-taker address + await etomicSwapMakerV2.connect(accounts[2]).spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const spendTx = await takerSwapRunner.spendMakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const spendReceipt = await spendTx.wait(); + const gasUsed = ethers.parseUnits(spendReceipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapMakerV2.makerPayments(id); + + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // should not allow to spend again + await takerSwapRunner.spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to spend ERC20 maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, // taker + takerSecretHash, + makerSecretHash, + paymentLockTime + ]; + + const makerSwapRunner = etomicSwapMakerV2.connect(accounts[0]); + const takerSwapRunner = etomicSwapMakerV2.connect(accounts[1]); + + // Make the ERC20 payment + await token.approve(etomicSwapMakerV2.target, ethers.parseEther('1')); + await makerSwapRunner.erc20MakerPayment(...payment_params).should.be.fulfilled; + + const contractBalance = await token.balanceOf(etomicSwapMakerV2.target); + expect(contractBalance).to.equal(ethers.parseEther('1')); + + const spendParamsInvalidSecret = [ + id, + ethers.parseEther('1'), + accounts[0].address, + takerSecretHash, + invalidSecret, + token.target, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParamsInvalidAmount = [ + id, + ethers.parseEther('0.9'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidAmount).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + ethers.parseEther('1'), + accounts[0].address, // maker + takerSecretHash, + makerSecret, + token.target, + ]; + + // should not allow to spend from non-taker address + await etomicSwapMakerV2.connect(accounts[2]).spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await token.balanceOf(accounts[1].address); + + const gasPrice = ethers.parseUnits('100', 'gwei'); + await takerSwapRunner.spendMakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[1].address); + // Check taker balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapMakerV2.makerPayments(id); + + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // should not allow to spend again + await takerSwapRunner.spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ETH payment after locktime', async function() { + const lockTime = (await ethers.provider.getBlock('latest')).timestamp + 1000; + const params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapMakerV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapMakerV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams) + .should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to refund before locktime + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + let invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + await etomicSwapRunner0.refundMakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapMakerV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC20 payment after locktime', async function() { + const lockTime = await currentEvmTime() + 1000; + const params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapMakerV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapMakerV2.connect(accounts[1]); + + await token.approve(etomicSwapMakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20MakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundMakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check maker balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapMakerV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ETH payment using taker secret', async function() { + const lockTime = (await ethers.provider.getBlock('latest')).timestamp + 1000; + const params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapMakerV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapMakerV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams) + .should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + let invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + await etomicSwapRunner0.refundMakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapMakerV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC20 payment using taker secret', async function() { + const lockTime = await currentEvmTime() + 1000; + const params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapMakerV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapMakerV2.connect(accounts[1]); + + await token.approve(etomicSwapMakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20MakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundMakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check maker balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapMakerV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); +}); diff --git a/test/EtomicSwapNft.js b/test/EtomicSwapNft.js new file mode 100644 index 0000000..b549d57 --- /dev/null +++ b/test/EtomicSwapNft.js @@ -0,0 +1,1300 @@ +const { + expect +} = require("chai"); +const { + ethers +} = require("hardhat"); +const crypto = require('crypto'); +const {AbiCoder} = require("ethers"); + +require('chai') + .use(require('chai-as-promised')) + .should(); + +const INVALID_HASH = 'Invalid paymentHash'; +const INVALID_PAYMENT_STATE_SENT = 'Invalid payment state. Must be PaymentSent'; +const INVALID_PAYMENT_STATE_APPROVED = 'Invalid payment state. Must be TakerApproved'; +const REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment refund lock time'; +const PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment pre-approve lock time'; + +/** + * Advances the Ethereum Virtual Machine (EVM) time by a specified amount and then mines a new block. + * + * @param {number} increaseAmount The amount of time to advance in seconds. + * + * This function is used in Ethereum smart contract testing to simulate the passage of time. In the EVM, + * time is measured based on block timestamps. The 'evm_increaseTime' method increases the EVM's internal + * clock, but this change only affects the next mined block. Therefore, 'evm_mine' is called immediately + * afterwards to mine a new block, ensuring that the blockchain's timestamp is updated to reflect the time + * change. This approach is essential for testing time-dependent contract features like lock periods or deadlines. + */ +async function advanceTimeAndMine(increaseAmount) { + await ethers.provider.send("evm_increaseTime", [increaseAmount]); + await ethers.provider.send("evm_mine"); +} + +async function currentEvmTime() { + const block = await ethers.provider.getBlock("latest"); + return block.timestamp; +} + +const id = '0x' + crypto.randomBytes(32).toString('hex'); +const [TAKER_PAYMENT_UNINITIALIZED, TAKER_PAYMENT_SENT, TAKER_PAYMENT_APPROVED, MAKER_SPENT, TAKER_REFUNDED] = [0, 1, 2, 3, 4]; +const [MAKER_PAYMENT_UNINITIALIZED, MAKER_PAYMENT_SENT, TAKER_SPENT, MAKER_REFUNDED] = [0, 1, 2, 3]; + +const takerSecret = crypto.randomBytes(32); +const takerSecretHash = '0x' + crypto.createHash('sha256').update(takerSecret).digest('hex'); + +const makerSecret = crypto.randomBytes(32); +const makerSecretHash = '0x' + crypto.createHash('sha256').update(makerSecret).digest('hex'); + +const invalidSecret = crypto.randomBytes(32); + +const zeroAddr = '0x0000000000000000000000000000000000000000'; +const dexFeeAddr = '0x8888888888888888888888888888888888888888'; + +describe("etomicSwapNft", function() { + + beforeEach(async function() { + accounts = await ethers.getSigners(); + + EtomicSwapNft = await ethers.getContractFactory("EtomicSwapNft"); + etomicSwapNft = await EtomicSwapNft.deploy(dexFeeAddr); + await etomicSwapNft.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + Erc721Token = await ethers.getContractFactory("Erc721Token"); + erc721token = await Erc721Token.deploy("MyNFT", "MNFT"); + await erc721token.waitForDeployment(); + + Erc1155Token = await ethers.getContractFactory("Erc1155Token"); + erc1155token = await Erc1155Token.deploy("uri"); + await erc1155token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther('100')); + }); + + it('should create contract with uninitialized payments', async function() { + const takerPayment = await etomicSwapNft.takerPayments(id); + expect(Number(takerPayment[3])).to.equal(TAKER_PAYMENT_UNINITIALIZED); + + const makerPayment = await etomicSwapNft.makerPayments(id); + expect(Number(makerPayment[2])).to.equal(MAKER_PAYMENT_UNINITIALIZED); + }); + + it('should allow maker to send ERC721 payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; // Assuming token ID 1 is minted to accounts[0] in Erc721Token contract + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + const makerErc721Runner0 = erc721token.connect(accounts[0]); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + // Explicitly specify the method signature. + const tx = await makerErc721Runner0['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapNft.target, tokenId, data).should.be.fulfilled; + const receipt = await tx.wait(); + console.log(`Gas Used: ${receipt.gasUsed.toString()}`); + + // Check the payment lockTime and state + const payment = await etomicSwapNft.makerPayments(id); + expect(Number(payment[1])).to.equal(paymentLockTime); + expect(Number(payment[2])).to.equal(MAKER_PAYMENT_SENT); + + // Should not allow to send again ( reverted with custom error ERC721InsufficientApproval ) + await expect(makerErc721Runner0['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapNft.target, tokenId, data)).to.be.rejectedWith("ERC721InsufficientApproval"); + }); + + it('should allow maker to send ERC1155 payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 2; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + const makerErc1155Runner0 = erc1155token.connect(accounts[0]); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + const tx = await makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, amountToSend, data).should.be.fulfilled; + const receipt = await tx.wait(); + console.log(`Gas Used: ${receipt.gasUsed.toString()}`); + + // Check the payment lockTime and state + const payment = await etomicSwapNft.makerPayments(id); + expect(Number(payment[1])).to.equal(paymentLockTime); + expect(Number(payment[2])).to.equal(MAKER_PAYMENT_SENT); + + // Check the balance of the token in the swap contract + const tokenBalance = await erc1155token.balanceOf(etomicSwapNft.target, tokenId); + expect(tokenBalance).to.equal(BigInt(amountToSend)); + + // Check sending same params again - should fail + await expect(makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, amountToSend, data)).to.be.rejectedWith("ERC1155InsufficientBalance"); + + // Maker should be capable to send more tokens, if they have it. Note: Check Erc1155.sol file. By default, ERC1155 is minted with 3 value. + const id1 = '0x' + crypto.randomBytes(32).toString('hex'); + const data1 = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id1, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + await makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, 1, data1).should.be.fulfilled; + + // Check sending more tokens than the sender owns - should fail + const id2 = '0x' + crypto.randomBytes(32).toString('hex'); + const data2 = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id2, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + await expect(makerErc1155Runner0.safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, amountToSend, data2)).to.be.rejectedWith("ERC1155InsufficientBalance"); + }); + + it('should allow taker to spend ERC721 maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + // Explicitly specify the method signature. + await erc721token.connect(accounts[0])['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapNft.target, tokenId, data).should.be.fulfilled; + + // Check the ownership of the token before Taker spend Maker ERC721 payment - should be owned by Swap NFT contract + const tokenOwnerBeforeTakerSpend = await erc721token.ownerOf(tokenId); + expect(tokenOwnerBeforeTakerSpend).to.equal(etomicSwapNft.target); + + const takerSwapRunner = etomicSwapNft.connect(accounts[1]); + + const spendParamsInvalidSecret = [ + id, + accounts[0].address, + takerSecretHash, + invalidSecret, + erc721token.target, + tokenId + ]; + // Attempt to spend with invalid secret - should fail + await takerSwapRunner.spendErc721MakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + accounts[0].address, + takerSecretHash, + makerSecret, + erc721token.target, + tokenId + ]; + + // should not allow to spend from non-taker address even with valid secret + await etomicSwapNft.connect(accounts[2]).spendErc721MakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // Successful spend by Taker with valid secret + await takerSwapRunner.spendErc721MakerPayment(...spendParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapNft.makerPayments(id); + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // Check the ownership of the token - should be transferred to the Taker (accounts[1]) + const tokenOwner = await erc721token.ownerOf(tokenId); + expect(tokenOwner).to.equal(accounts[1].address); + + // should not allow to spend again + await takerSwapRunner.spendErc721MakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }) + + it('should allow taker to spend ERC1155 maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 2; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, paymentLockTime] + ); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + await erc1155token.connect(accounts[0]).safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, amountToSend, data).should.be.fulfilled; + + // Check the balance of the token before Taker spend Maker ERC1155 payment - should be in Swap NFT contract + let tokenBalanceBeforeTakerSpend = await erc1155token.balanceOf(etomicSwapNft.target, tokenId); + expect(tokenBalanceBeforeTakerSpend).to.equal(BigInt(amountToSend)); + + const takerSwapRunner = etomicSwapNft.connect(accounts[1]); + + const spendParamsInvalidSecret = [ + id, + accounts[0].address, + takerSecretHash, + invalidSecret, + erc1155token.target, + tokenId, + amountToSend + ]; + // Attempt to spend with invalid secret - should fail + await takerSwapRunner.spendErc1155MakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + accounts[0].address, + takerSecretHash, + makerSecret, + erc1155token.target, + tokenId, + amountToSend + ]; + + // should not allow to spend from non-taker address even with valid secret + await etomicSwapNft.connect(accounts[2]).spendErc1155MakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // Successful spend by Taker with valid secret + await takerSwapRunner.spendErc1155MakerPayment(...spendParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapNft.makerPayments(id); + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // Check the balance of the token - should be transferred to the Taker (accounts[1]) + let tokenBalance = await erc1155token.balanceOf(accounts[1].address, tokenId); + expect(tokenBalance).to.equal(BigInt(amountToSend)); + + // Check that the Swap NFT contract no longer holds the tokens + tokenBalance = await erc1155token.balanceOf(etomicSwapNft.target, tokenId); + expect(tokenBalance).to.equal(BigInt(0)); + + // should not allow to spend again + await takerSwapRunner.spendErc1155MakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC721 payment after locktime', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapNft.connect(accounts[0]); + + // Not allow maker to refund if payment was not sent + const refundParams = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + erc721token.target, + tokenId + ]; + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + // Explicitly specify the method signature. + await erc721token.connect(accounts[0])['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapNft.target, tokenId, data).should.be.fulfilled; + + // Not allow to refund before locktime + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-maker address + await etomicSwapNft.connect(accounts[1]).refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Successful refund by maker after locktime + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapNft.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow maker to refund again + await makerSwapRunner.refundErc721MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC1155 payment after locktime', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 3; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapNft.connect(accounts[0]); + + const refundParams = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + erc1155token.target, + tokenId, + amountToSend + ]; + + // Not allow maker to refund if payment was not sent + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + await erc1155token.connect(accounts[0]).safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, amountToSend, data).should.be.fulfilled; + + // Not allow to refund before locktime + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-maker address + await etomicSwapNft.connect(accounts[1]).refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH) + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + erc1155token.target, + tokenId, + 2 + ]; + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Successful refund by maker after locktime + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapNft.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Check the balance of the token - should be back to the maker (accounts[0]) + const tokenBalance = await erc1155token.balanceOf(accounts[0].address, tokenId); + expect(tokenBalance).to.equal(BigInt(amountToSend)); + + // Do not allow to refund again + await makerSwapRunner.refundErc1155MakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC721 payment using taker secret', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc721token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapNft.connect(accounts[0]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + accounts[1].address, + takerSecret, + makerSecretHash, + erc721token.target, + tokenId + ]; + await makerSwapRunner.refundErc721MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC721 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + // Explicitly specify the method signature. + await erc721token.connect(accounts[0])['safeTransferFrom(address,address,uint256,bytes)'](accounts[0].address, etomicSwapNft.target, tokenId, data).should.be.fulfilled; + + // Not allow to call refund from non-maker address + await etomicSwapNft.connect(accounts[1]).refundErc721MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Successful refund by maker using taker secret + await makerSwapRunner.refundErc721MakerPaymentSecret(...refundParams).should.be.fulfilled; + + // Check the state of the payment + const payment = await etomicSwapNft.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow maker to refund again + await makerSwapRunner.refundErc721MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC1155 payment using taker secret', async function() { + const lockTime = await currentEvmTime() + 1000; + const tokenId = 1; // Token ID used in Erc1155Token contract + const amountToSend = 3; // Amount of tokens to send + + const abiCoder = new AbiCoder(); + const data = abiCoder.encode( + ['bytes32', 'address', 'address', 'bytes32', 'bytes32','uint32'], + [id, accounts[1].address, erc1155token.target, takerSecretHash, makerSecretHash, lockTime] + ); + + let makerSwapRunner = etomicSwapNft.connect(accounts[0]); + + const refundParams = [ + id, + accounts[1].address, + takerSecret, + makerSecretHash, + erc1155token.target, + tokenId, + amountToSend + ]; + + // Not allow maker to refund if payment was not sent + await makerSwapRunner.refundErc1155MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the Maker ERC1155 payment. Call safeTransferFrom directly to transfer the token to the EtomicSwapNft contract. + await erc1155token.connect(accounts[0]).safeTransferFrom(accounts[0].address, etomicSwapNft.target, tokenId, amountToSend, data).should.be.fulfilled; + + // Not allow to call refund from non-maker address + await etomicSwapNft.connect(accounts[1]).refundErc1155MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + accounts[1].address, + takerSecret, + makerSecretHash, + erc1155token.target, + tokenId, + 2 + ]; + await makerSwapRunner.refundErc1155MakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + await makerSwapRunner.refundErc1155MakerPaymentSecret(...refundParams).should.be.fulfilled; + + // Successful refund by maker using taker secret + const payment = await etomicSwapNft.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Do not allow to refund again + await makerSwapRunner.refundErc1155MakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to send ETH payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const params = [ + id, + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapNft.connect(accounts[0]).ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const payment = await etomicSwapNft.takerPayments(id); + + expect(Number(payment[1])).to.equal(immediateRefundLockTime); + expect(Number(payment[2])).to.equal(paymentLockTime); + expect(Number(payment[3])).to.equal(TAKER_PAYMENT_SENT); + + // Check that it should not allow to send again + await etomicSwapNft.connect(accounts[0]).ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.rejectedWith("Taker payment is already initialized"); + }); + + it('should allow taker to send ERC20 payment', async function() { + const currentTime = await currentEvmTime(); + + const immediateRefundLockTime = currentTime + 10; + const paymentLockTime = currentTime + 100; + + const paymentParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapNft.connect(accounts[0]); + + await token.approve(etomicSwapNft.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20TakerPayment(...paymentParams).should.be.fulfilled; + + // Check contract token balance + const balance = await token.balanceOf(etomicSwapNft.target); + expect(balance).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapNft.takerPayments(id); + + // Check locktime and status + expect(payment[1]).to.equal(BigInt(immediateRefundLockTime)); + expect(payment[2]).to.equal(BigInt(paymentLockTime)); + expect(payment[3]).to.equal(BigInt(TAKER_PAYMENT_SENT)); + + // Should not allow to send payment again + await etomicSwapRunner0.erc20TakerPayment(...paymentParams).should.be.rejectedWith("ERC20 v2 payment is already initialized"); + }); + + it('should allow maker to spend ETH taker payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const paymentParams = [ + id, + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapNft.connect(accounts[0]).ethTakerPayment(...paymentParams, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const spendParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + // should not allow to spend before payment is approved by taker + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapNft.connect(accounts[0]).takerPaymentApprove(...approveParams).should.be.fulfilled; + + // should not allow to spend from invalid address + await etomicSwapNft.connect(accounts[0]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // should not allow to spend with invalid amounts + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const spendTx = await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const spendReceipt = await spendTx.wait(); + const gasUsed = ethers.parseUnits(spendReceipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('0.9')); + + const dexFeeAddrBalance = await ethers.provider.getBalance(dexFeeAddr); + expect(dexFeeAddrBalance).to.equal(ethers.parseEther('0.1')); + + const payment = await etomicSwapNft.takerPayments(id); + + expect(Number(payment[3])).to.equal(MAKER_SPENT); + + // Do not allow to spend again + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + }); + + it('should allow maker to spend ERC20 taker payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const paymentParams = [ + id, + ethers.parseEther('0.9'), // amount + ethers.parseEther('0.1'), // dexFee + token.target, + accounts[1].address, // receiver + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + + // Make the ERC20 payment + await token.approve(etomicSwapNft.target, ethers.parseEther('1')); + await etomicSwapNft.connect(accounts[0]).erc20TakerPayment(...paymentParams).should.be.fulfilled; + + const contractBalance = await token.balanceOf(etomicSwapNft.target); + expect(contractBalance).to.equal(ethers.parseEther('1')); + + const spendParams = [ + id, + ethers.parseEther('0.9'), // amount + ethers.parseEther('0.1'), // dexFee + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, // tokenAddress + ]; + + // should not allow to spend before taker's approval + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapNft.connect(accounts[0]).takerPaymentApprove(...approveParams).should.be.fulfilled; + + // should not allow to spend from invalid address + await etomicSwapNft.connect(accounts[0]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // should not allow to spend with invalid amounts + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await token.balanceOf(accounts[1].address); + + const gasPrice = ethers.parseUnits('100', 'gwei'); + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[1].address); + // Check receiver balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('0.9')); + + const dexFeeAddrBalance = await token.balanceOf(dexFeeAddr); + expect(dexFeeAddrBalance).to.equal(ethers.parseEther('0.1')); + + const payment = await etomicSwapNft.takerPayments(id); + + expect(Number(payment[3])).to.equal(MAKER_SPENT); + + // Do not allow to spend again + await etomicSwapNft.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + }); + + it('should allow taker to refund approved ETH payment after locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime + ]; + + let takerSwapRunner = etomicSwapNft.connect(accounts[0]); + let makerSwapRunner = etomicSwapNft.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await takerSwapRunner.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + await takerSwapRunner.takerPaymentApprove(...approveParams).should.be.fulfilled; + + // Not allow to refund before locktime + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-taker address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await takerSwapRunner.refundTakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapNft.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund non-approved ETH payment only after pre-approve locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapNft.connect(accounts[0]); + let makerSwapRunner = etomicSwapNft.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await takerSwapRunner.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + await advanceTimeAndMine(2000); + + // Not allow to refund before pre-approve locktime + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(3000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await takerSwapRunner.refundTakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapNft.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund approved ERC20 payment after locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapNft.connect(accounts[0]); + let makerSwapRunner = etomicSwapNft.connect(accounts[1]); + + await token.approve(etomicSwapNft.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(takerSwapRunner.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.takerPaymentApprove(...approveParams).should.be.fulfilled; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapNft.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund non-approved ERC20 payment only after pre-approve locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapNft.connect(accounts[0]); + let makerSwapRunner = etomicSwapNft.connect(accounts[1]); + + await token.approve(etomicSwapNft.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(takerSwapRunner.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await advanceTimeAndMine(2000); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapNft.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund ETH payment using secret', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapNft.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapNft.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapNft.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund ERC20 payment using secret', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapNft.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapNft.connect(accounts[1]); + + await token.approve(etomicSwapNft.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapNft.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); +}); \ No newline at end of file diff --git a/test/EtomicSwapTakerV2Test.js b/test/EtomicSwapTakerV2Test.js new file mode 100644 index 0000000..e80f458 --- /dev/null +++ b/test/EtomicSwapTakerV2Test.js @@ -0,0 +1,885 @@ +const { + expect +} = require("chai"); +const { + ethers +} = require("hardhat"); +const crypto = require('crypto'); + +require('chai') + .use(require('chai-as-promised')) + .should(); + +const INVALID_HASH = 'Invalid paymentHash'; +const INVALID_PAYMENT_STATE_SENT = 'Invalid payment state. Must be PaymentSent'; +const INVALID_PAYMENT_STATE_APPROVED = 'Invalid payment state. Must be TakerApproved'; +const REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment refund lock time'; +const PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment pre-approve lock time'; + +/** + * Advances the Ethereum Virtual Machine (EVM) time by a specified amount and then mines a new block. + * + * @param {number} increaseAmount The amount of time to advance in seconds. + * + * This function is used in Ethereum smart contract testing to simulate the passage of time. In the EVM, + * time is measured based on block timestamps. The 'evm_increaseTime' method increases the EVM's internal + * clock, but this change only affects the next mined block. Therefore, 'evm_mine' is called immediately + * afterwards to mine a new block, ensuring that the blockchain's timestamp is updated to reflect the time + * change. This approach is essential for testing time-dependent contract features like lock periods or deadlines. + */ +async function advanceTimeAndMine(increaseAmount) { + await ethers.provider.send("evm_increaseTime", [increaseAmount]); + await ethers.provider.send("evm_mine"); +} + +async function currentEvmTime() { + const block = await ethers.provider.getBlock("latest"); + return block.timestamp; +} + +const id = '0x' + crypto.randomBytes(32).toString('hex'); +const [TAKER_PAYMENT_UNINITIALIZED, TAKER_PAYMENT_SENT, TAKER_PAYMENT_APPROVED, MAKER_SPENT, TAKER_REFUNDED] = [0, 1, 2, 3, 4]; + +const takerSecret = crypto.randomBytes(32); +const takerSecretHash = '0x' + crypto.createHash('sha256').update(takerSecret).digest('hex'); + +const makerSecret = crypto.randomBytes(32); +const makerSecretHash = '0x' + crypto.createHash('sha256').update(makerSecret).digest('hex'); + +const invalidSecret = crypto.randomBytes(32); + +const zeroAddr = '0x0000000000000000000000000000000000000000'; +const dexFeeAddr = '0x9999999999999999999999999999999999999999'; + +describe("EtomicSwapTakerV2", function() { + + beforeEach(async function() { + accounts = await ethers.getSigners(); + + EtomicSwapTakerV2 = await ethers.getContractFactory("EtomicSwapTakerV2"); + etomicSwapTakerV2 = await EtomicSwapTakerV2.deploy(dexFeeAddr); + await etomicSwapTakerV2.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther('100')); + }); + + it('should create contract with uninitialized payments', async function() { + const takerPayment = await etomicSwapTakerV2.takerPayments(id); + expect(Number(takerPayment[3])).to.equal(TAKER_PAYMENT_UNINITIALIZED); + }); + + it('should allow taker to send ETH payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const params = [ + id, + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapTakerV2.connect(accounts[0]).ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const payment = await etomicSwapTakerV2.takerPayments(id); + + expect(Number(payment[1])).to.equal(immediateRefundLockTime); + expect(Number(payment[2])).to.equal(paymentLockTime); + expect(Number(payment[3])).to.equal(TAKER_PAYMENT_SENT); + + // Check that it should not allow to send again + await etomicSwapTakerV2.connect(accounts[0]).ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.rejectedWith("Taker payment is already initialized"); + }); + + it('should allow taker to send ERC20 payment', async function() { + const currentTime = await currentEvmTime(); + + const immediateRefundLockTime = currentTime + 10; + const paymentLockTime = currentTime + 100; + + const payment_params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapTakerV2.connect(accounts[0]); + + await token.approve(etomicSwapTakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20TakerPayment(...payment_params).should.be.fulfilled; + + // Check contract token balance + const balance = await token.balanceOf(etomicSwapTakerV2.target); + expect(balance).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapTakerV2.takerPayments(id); + + // Check locktime and status + expect(payment[1]).to.equal(BigInt(immediateRefundLockTime)); + expect(payment[2]).to.equal(BigInt(paymentLockTime)); + expect(payment[3]).to.equal(BigInt(TAKER_PAYMENT_SENT)); + + // Should not allow to send payment again + await etomicSwapRunner0.erc20TakerPayment(...payment_params).should.be.rejectedWith("ERC20 v2 payment is already initialized"); + }); + + it('should allow maker to spend ETH taker payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapTakerV2.connect(accounts[0]).ethTakerPayment(...payment_params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const spendParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + // should not allow to spend before payment is approved by taker + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapTakerV2.connect(accounts[0]).takerPaymentApprove(...approveParams).should.be.fulfilled; + + // should not allow to spend from invalid address + await etomicSwapTakerV2.connect(accounts[0]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // should not allow to spend with invalid amounts + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const spendTx = await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const spendReceipt = await spendTx.wait(); + const gasUsed = ethers.parseUnits(spendReceipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('0.9')); + + const dexFeeAddrBalance = await ethers.provider.getBalance(dexFeeAddr); + expect(dexFeeAddrBalance).to.equal(ethers.parseEther('0.1')); + + const payment = await etomicSwapTakerV2.takerPayments(id); + + expect(Number(payment[3])).to.equal(MAKER_SPENT); + + // Do not allow to spend again + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + }); + + it('should allow maker to spend ERC20 taker payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + ethers.parseEther('0.9'), // amount + ethers.parseEther('0.1'), // dexFee + token.target, + accounts[1].address, // receiver + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + + // Make the ERC20 payment + await token.approve(etomicSwapTakerV2.target, ethers.parseEther('1')); + await etomicSwapTakerV2.connect(accounts[0]).erc20TakerPayment(...payment_params).should.be.fulfilled; + + const contractBalance = await token.balanceOf(etomicSwapTakerV2.target); + expect(contractBalance).to.equal(ethers.parseEther('1')); + + const spendParams = [ + id, + ethers.parseEther('0.9'), // amount + ethers.parseEther('0.1'), // dexFee + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, // tokenAddress + ]; + + // should not allow to spend before taker's approval + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapTakerV2.connect(accounts[0]).takerPaymentApprove(...approveParams).should.be.fulfilled; + + // should not allow to spend from invalid address + await etomicSwapTakerV2.connect(accounts[0]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // should not allow to spend with invalid amounts + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await token.balanceOf(accounts[1].address); + + const gasPrice = ethers.parseUnits('100', 'gwei'); + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[1].address); + // Check receiver balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('0.9')); + + const dexFeeAddrBalance = await token.balanceOf(dexFeeAddr); + expect(dexFeeAddrBalance).to.equal(ethers.parseEther('0.1')); + + const payment = await etomicSwapTakerV2.takerPayments(id); + + expect(Number(payment[3])).to.equal(MAKER_SPENT); + + // Do not allow to spend again + await etomicSwapTakerV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + }); + + it('should allow taker to refund approved ETH payment after locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapTakerV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapTakerV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await takerSwapRunner.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await takerSwapRunner.takerPaymentApprove(...approveParams).should.be.fulfilled; + + // Not allow to refund before locktime + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await takerSwapRunner.refundTakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapTakerV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund non-approved ETH payment only after pre-approve locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapTakerV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapTakerV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await takerSwapRunner.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + await advanceTimeAndMine(2000); + + // Not allow to refund before pre-approve locktime + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(3000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await takerSwapRunner.refundTakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapTakerV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund approved ERC20 payment after locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapTakerV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapTakerV2.connect(accounts[1]); + + await token.approve(etomicSwapTakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(takerSwapRunner.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.takerPaymentApprove(...approveParams).should.be.fulfilled; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapTakerV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund non-approved ERC20 payment only after pre-approve locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapTakerV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapTakerV2.connect(accounts[1]); + + await token.approve(etomicSwapTakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(takerSwapRunner.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await advanceTimeAndMine(2000); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapTakerV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund ETH payment using secret', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapTakerV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapTakerV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapTakerV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund ERC20 payment using secret', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapTakerV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapTakerV2.connect(accounts[1]); + + await token.approve(etomicSwapTakerV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapTakerV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); +}); diff --git a/test/EtomicSwapV2.js b/test/EtomicSwapV2.js new file mode 100644 index 0000000..e1dee14 --- /dev/null +++ b/test/EtomicSwapV2.js @@ -0,0 +1,1369 @@ +const { + expect +} = require("chai"); +const { + ethers +} = require("hardhat"); +const crypto = require('crypto'); +const RIPEMD160 = require('ripemd160'); + +require('chai') + .use(require('chai-as-promised')) + .should(); + +const INVALID_HASH = 'Invalid paymentHash'; +const INVALID_PAYMENT_STATE_SENT = 'Invalid payment state. Must be PaymentSent'; +const INVALID_PAYMENT_STATE_APPROVED = 'Invalid payment state. Must be TakerApproved'; +const REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment refund lock time'; +const PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED = 'Current timestamp didn\'t exceed payment pre-approve lock time'; + +/** + * Advances the Ethereum Virtual Machine (EVM) time by a specified amount and then mines a new block. + * + * @param {number} increaseAmount The amount of time to advance in seconds. + * + * This function is used in Ethereum smart contract testing to simulate the passage of time. In the EVM, + * time is measured based on block timestamps. The 'evm_increaseTime' method increases the EVM's internal + * clock, but this change only affects the next mined block. Therefore, 'evm_mine' is called immediately + * afterwards to mine a new block, ensuring that the blockchain's timestamp is updated to reflect the time + * change. This approach is essential for testing time-dependent contract features like lock periods or deadlines. + */ +async function advanceTimeAndMine(increaseAmount) { + await ethers.provider.send("evm_increaseTime", [increaseAmount]); + await ethers.provider.send("evm_mine"); +} + +async function currentEvmTime() { + const block = await ethers.provider.getBlock("latest"); + return block.timestamp; +} + +const id = '0x' + crypto.randomBytes(32).toString('hex'); +const [TAKER_PAYMENT_UNINITIALIZED, TAKER_PAYMENT_SENT, TAKER_PAYMENT_APPROVED, MAKER_SPENT, TAKER_REFUNDED] = [0, 1, 2, 3, 4]; +const [MAKER_PAYMENT_UNINITIALIZED, MAKER_PAYMENT_SENT, TAKER_SPENT, MAKER_REFUNDED] = [0, 1, 2, 3]; + +const takerSecret = crypto.randomBytes(32); +const takerSecretHash = '0x' + crypto.createHash('sha256').update(takerSecret).digest('hex'); + +const makerSecret = crypto.randomBytes(32); +const makerSecretHash = '0x' + crypto.createHash('sha256').update(makerSecret).digest('hex'); + +const invalidSecret = crypto.randomBytes(32); + +const zeroAddr = '0x0000000000000000000000000000000000000000'; +const dexFeeAddr = '0x7777777777777777777777777777777777777777'; + +describe("EtomicSwapV2", function() { + + beforeEach(async function() { + accounts = await ethers.getSigners(); + + EtomicSwapV2 = await ethers.getContractFactory("EtomicSwapV2"); + etomicSwapV2 = await EtomicSwapV2.deploy(dexFeeAddr); + await etomicSwapV2.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther('100')); + }); + + it('should create contract with uninitialized payments', async function() { + const takerPayment = await etomicSwapV2.takerPayments(id); + expect(Number(takerPayment[3])).to.equal(TAKER_PAYMENT_UNINITIALIZED); + + const makerPayment = await etomicSwapV2.makerPayments(id); + expect(Number(makerPayment[2])).to.equal(MAKER_PAYMENT_UNINITIALIZED); + }); + + it('should allow maker to send ETH payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapV2.connect(accounts[0]).ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const payment = await etomicSwapV2.makerPayments(id); + + expect(Number(payment[1])).to.equal(paymentLockTime); + expect(Number(payment[2])).to.equal(MAKER_PAYMENT_SENT); + + // Check that it should not allow to send again + await etomicSwapV2.connect(accounts[0]).ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.rejectedWith("Maker payment is already initialized"); + }); + + it('should allow maker to send ERC20 payment', async function() { + const currentTime = await currentEvmTime(); + + const paymentLockTime = currentTime + 100; + + const payment_params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20MakerPayment(...payment_params).should.be.fulfilled; + + // Check contract token balance + const balance = await token.balanceOf(etomicSwapV2.target); + expect(balance).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapV2.makerPayments(id); + + // Check locktime and status + expect(payment[1]).to.equal(BigInt(paymentLockTime)); + expect(payment[2]).to.equal(BigInt(MAKER_PAYMENT_SENT)); + + // Should not allow to send payment again + await etomicSwapRunner0.erc20MakerPayment(...payment_params).should.be.rejectedWith("Maker payment is already initialized"); + }); + + it('should allow taker to spend ETH maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + paymentLockTime + ]; + + const makerSwapRunner = etomicSwapV2.connect(accounts[0]); + const takerSwapRunner = etomicSwapV2.connect(accounts[1]); + + // Make the ETH payment + await makerSwapRunner.ethMakerPayment(...payment_params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const spendParamsInvalidSecret = [ + id, + ethers.parseEther('1'), + accounts[0].address, + takerSecretHash, + invalidSecret, + zeroAddr, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParamsInvalidAmount = [ + id, + ethers.parseEther('0.9'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidAmount).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + ethers.parseEther('1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + // should not allow to spend from non-taker address + await etomicSwapV2.connect(accounts[2]).spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const spendTx = await takerSwapRunner.spendMakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const spendReceipt = await spendTx.wait(); + const gasUsed = ethers.parseUnits(spendReceipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapV2.makerPayments(id); + + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // should not allow to spend again + await takerSwapRunner.spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to spend ERC20 maker payment', async function() { + let currentTime = await currentEvmTime(); + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, // taker + takerSecretHash, + makerSecretHash, + paymentLockTime + ]; + + const makerSwapRunner = etomicSwapV2.connect(accounts[0]); + const takerSwapRunner = etomicSwapV2.connect(accounts[1]); + + // Make the ERC20 payment + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + await makerSwapRunner.erc20MakerPayment(...payment_params).should.be.fulfilled; + + const contractBalance = await token.balanceOf(etomicSwapV2.target); + expect(contractBalance).to.equal(ethers.parseEther('1')); + + const spendParamsInvalidSecret = [ + id, + ethers.parseEther('1'), + accounts[0].address, + takerSecretHash, + invalidSecret, + token.target, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidSecret).should.be.rejectedWith(INVALID_HASH); + + const spendParamsInvalidAmount = [ + id, + ethers.parseEther('0.9'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + await takerSwapRunner.spendMakerPayment(...spendParamsInvalidAmount).should.be.rejectedWith(INVALID_HASH); + + const spendParams = [ + id, + ethers.parseEther('1'), + accounts[0].address, // maker + takerSecretHash, + makerSecret, + token.target, + ]; + + // should not allow to spend from non-taker address + await etomicSwapV2.connect(accounts[2]).spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await token.balanceOf(accounts[1].address); + + const gasPrice = ethers.parseUnits('100', 'gwei'); + await takerSwapRunner.spendMakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[1].address); + // Check taker balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapV2.makerPayments(id); + + expect(Number(payment[2])).to.equal(TAKER_SPENT); + + // should not allow to spend again + await takerSwapRunner.spendMakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ETH payment after locktime', async function() { + const lockTime = (await ethers.provider.getBlock('latest')).timestamp + 1000; + const params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams) + .should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to refund before locktime + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + let invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + await etomicSwapRunner0.refundMakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC20 payment after locktime', async function() { + const lockTime = await currentEvmTime() + 1000; + const params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapV2.connect(accounts[1]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20MakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundMakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check maker balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundMakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ETH payment using taker secret', async function() { + const lockTime = (await ethers.provider.getBlock('latest')).timestamp + 1000; + const params = [ + id, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams) + .should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethMakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + let invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + await etomicSwapRunner0.refundMakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow maker to refund ERC20 payment using taker secret', async function() { + const lockTime = await currentEvmTime() + 1000; + const params = [ + id, + ethers.parseEther('1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + lockTime + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapV2.connect(accounts[1]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20MakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.9'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundMakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check maker balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.makerPayments(id); + expect(payment.state).to.equal(BigInt(MAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundMakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to send ETH payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const params = [ + id, + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapV2.connect(accounts[0]).ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const payment = await etomicSwapV2.takerPayments(id); + + expect(Number(payment[1])).to.equal(immediateRefundLockTime); + expect(Number(payment[2])).to.equal(paymentLockTime); + expect(Number(payment[3])).to.equal(TAKER_PAYMENT_SENT); + + // Check that it should not allow to send again + await etomicSwapV2.connect(accounts[0]).ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.rejectedWith("Taker payment is already initialized"); + }); + + it('should allow taker to send ERC20 payment', async function() { + const currentTime = await currentEvmTime(); + + const immediateRefundLockTime = currentTime + 10; + const paymentLockTime = currentTime + 100; + + const payment_params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await etomicSwapRunner0.erc20TakerPayment(...payment_params).should.be.fulfilled; + + // Check contract token balance + const balance = await token.balanceOf(etomicSwapV2.target); + expect(balance).to.equal(ethers.parseEther('1')); + + const payment = await etomicSwapV2.takerPayments(id); + + // Check locktime and status + expect(payment[1]).to.equal(BigInt(immediateRefundLockTime)); + expect(payment[2]).to.equal(BigInt(paymentLockTime)); + expect(payment[3]).to.equal(BigInt(TAKER_PAYMENT_SENT)); + + // Should not allow to send payment again + await etomicSwapRunner0.erc20TakerPayment(...payment_params).should.be.rejectedWith("ERC20 v2 payment is already initialized"); + }); + + it('should allow maker to spend ETH taker payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + // Make the ETH payment + await etomicSwapV2.connect(accounts[0]).ethTakerPayment(...payment_params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const spendParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + // should not allow to spend before payment is approved by taker + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapV2.connect(accounts[0]).takerPaymentApprove(...approveParams).should.be.fulfilled; + + // should not allow to spend from invalid address + await etomicSwapV2.connect(accounts[0]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // should not allow to spend with invalid amounts + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[0].address, + takerSecretHash, + makerSecret, + zeroAddr, + ]; + + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await ethers.provider.getBalance(accounts[1].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const spendTx = await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const spendReceipt = await spendTx.wait(); + const gasUsed = ethers.parseUnits(spendReceipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[1].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('0.9')); + + const dexFeeAddrBalance = await ethers.provider.getBalance(dexFeeAddr); + expect(dexFeeAddrBalance).to.equal(ethers.parseEther('0.1')); + + const payment = await etomicSwapV2.takerPayments(id); + + expect(Number(payment[3])).to.equal(MAKER_SPENT); + + // Do not allow to spend again + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + }); + + it('should allow maker to spend ERC20 taker payment', async function() { + let currentTime = await currentEvmTime(); + const immediateRefundLockTime = currentTime + 100; + const paymentLockTime = currentTime + 100; + const payment_params = [ + id, + ethers.parseEther('0.9'), // amount + ethers.parseEther('0.1'), // dexFee + token.target, + accounts[1].address, // receiver + takerSecretHash, + makerSecretHash, + immediateRefundLockTime, + paymentLockTime + ]; + + // Make the ERC20 payment + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + await etomicSwapV2.connect(accounts[0]).erc20TakerPayment(...payment_params).should.be.fulfilled; + + const contractBalance = await token.balanceOf(etomicSwapV2.target); + expect(contractBalance).to.equal(ethers.parseEther('1')); + + const spendParams = [ + id, + ethers.parseEther('0.9'), // amount + ethers.parseEther('0.1'), // dexFee + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, // tokenAddress + ]; + + // should not allow to spend before taker's approval + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await etomicSwapV2.connect(accounts[0]).takerPaymentApprove(...approveParams).should.be.fulfilled; + + // should not allow to spend from invalid address + await etomicSwapV2.connect(accounts[0]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_HASH); + + // should not allow to spend with invalid amounts + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[0].address, + takerSecretHash, + makerSecret, + token.target, + ]; + + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + const balanceBefore = await token.balanceOf(accounts[1].address); + + const gasPrice = ethers.parseUnits('100', 'gwei'); + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...spendParams, { + gasPrice + }).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[1].address); + // Check receiver balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('0.9')); + + const dexFeeAddrBalance = await token.balanceOf(dexFeeAddr); + expect(dexFeeAddrBalance).to.equal(ethers.parseEther('0.1')); + + const payment = await etomicSwapV2.takerPayments(id); + + expect(Number(payment[3])).to.equal(MAKER_SPENT); + + // Do not allow to spend again + await etomicSwapV2.connect(accounts[1]).spendTakerPayment(...spendParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_APPROVED); + }); + + it('should allow taker to refund approved ETH payment after locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await takerSwapRunner.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr, + ]; + + await takerSwapRunner.takerPaymentApprove(...approveParams).should.be.fulfilled; + + // Not allow to refund before locktime + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await takerSwapRunner.refundTakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund non-approved ETH payment only after pre-approve locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await takerSwapRunner.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + await advanceTimeAndMine(2000); + + // Not allow to refund before pre-approve locktime + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED); + + // Simulate time passing to exceed the locktime + await advanceTimeAndMine(3000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + zeroAddr + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await takerSwapRunner.refundTakerPaymentTimelock(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund approved ERC20 payment after locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapV2.connect(accounts[1]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(takerSwapRunner.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + const approveParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.takerPaymentApprove(...approveParams).should.be.fulfilled; + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund non-approved ERC20 payment only after pre-approve locktime', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let takerSwapRunner = etomicSwapV2.connect(accounts[0]); + let makerSwapRunner = etomicSwapV2.connect(accounts[1]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(takerSwapRunner.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await advanceTimeAndMine(2000); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(PRE_APPROVE_REFUND_TIMESTAMP_NOT_REACHED); + + await advanceTimeAndMine(1000); + + // Not allow to call refund from non-sender address + await makerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecretHash, + makerSecretHash, + token.target, + ]; + + await takerSwapRunner.refundTakerPaymentTimelock(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await takerSwapRunner.refundTakerPaymentTimelock(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund ETH payment using secret', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.1'), // dexFee + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapV2.connect(accounts[1]); + + // Not allow to refund if payment was not sent + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + + // Make the ETH payment + await etomicSwapRunner0.ethTakerPayment(...params, { + value: ethers.parseEther('1') + }).should.be.fulfilled; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecret, + makerSecretHash, + zeroAddr, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await ethers.provider.getBalance(accounts[0].address); + const gasPrice = ethers.parseUnits('100', 'gwei'); + + const tx = await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams, { + gasPrice + }).should.be.fulfilled; + + const receipt = await tx.wait(); + const gasUsed = ethers.parseUnits(receipt.gasUsed.toString(), 'wei'); + const txFee = gasUsed * gasPrice; + + const balanceAfter = await ethers.provider.getBalance(accounts[0].address); + // Check sender balance + expect((balanceAfter - balanceBefore + txFee)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Not allow to refund again + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); + + it('should allow taker to refund ERC20 payment using secret', async function() { + const preApproveLockTime = await currentEvmTime() + 3000; + const paymentLockTime = await currentEvmTime() + 1000; + + const params = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + token.target, + accounts[1].address, + takerSecretHash, + makerSecretHash, + preApproveLockTime, + paymentLockTime, + ]; + + let etomicSwapRunner0 = etomicSwapV2.connect(accounts[0]); + let etomicSwapRunner1 = etomicSwapV2.connect(accounts[1]); + + await token.approve(etomicSwapV2.target, ethers.parseEther('1')); + // Make the ERC20 payment + await expect(etomicSwapRunner0.erc20TakerPayment(...params)).to.be.fulfilled; + + const refundParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + // Not allow to call refund from non-sender address + await etomicSwapRunner1.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_HASH); + + // Not allow to refund invalid amount + const invalidAmountParams = [ + id, + ethers.parseEther('0.8'), + ethers.parseEther('0.1'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidAmountParams).should.be.rejectedWith(INVALID_HASH); + + const invalidDexFeeParams = [ + id, + ethers.parseEther('0.9'), + ethers.parseEther('0.2'), + accounts[1].address, + takerSecret, + makerSecretHash, + token.target, + ]; + + await etomicSwapRunner0.refundTakerPaymentSecret(...invalidDexFeeParams).should.be.rejectedWith(INVALID_HASH); + + // Success refund + const balanceBefore = await token.balanceOf(accounts[0].address); + + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.fulfilled; + + const balanceAfter = await token.balanceOf(accounts[0].address); + + // Check sender balance + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + expect((balanceAfter - balanceBefore)).to.equal(ethers.parseEther('1')); + + // Check the state of the payment + const payment = await etomicSwapV2.takerPayments(id); + expect(payment.state).to.equal(BigInt(TAKER_REFUNDED)); + + // Do not allow to refund again + await etomicSwapRunner0.refundTakerPaymentSecret(...refundParams).should.be.rejectedWith(INVALID_PAYMENT_STATE_SENT); + }); +}); \ No newline at end of file diff --git a/test/SwapFeeManagerTest.js b/test/SwapFeeManagerTest.js new file mode 100644 index 0000000..678985e --- /dev/null +++ b/test/SwapFeeManagerTest.js @@ -0,0 +1,91 @@ +const { expect } = require("chai"); +const { ethers } = require("hardhat"); +require('chai').use(require('chai-as-promised')).should(); + +describe("SwapFeeManager", function () { + beforeEach(async function () { + // Resets the Hardhat Network to its initial state + await network.provider.send("hardhat_reset"); + accounts = await ethers.getSigners(); + + // Set balances for dexFeeWallet and burnFeeWallet to 0 + await network.provider.send("hardhat_setBalance", [accounts[2].address, "0x0"]); + await network.provider.send("hardhat_setBalance", [accounts[3].address, "0x0"]); + + SwapFeeManager = await ethers.getContractFactory("SwapFeeManager"); + swapFeeManager = await SwapFeeManager.deploy( + accounts[2].address, // dexFeeWallet + accounts[3].address // burnFeeWallet + ); + await swapFeeManager.waitForDeployment(); + + Token = await ethers.getContractFactory("Token"); + token = await Token.deploy(); + await token.waitForDeployment(); + + await token.transfer(accounts[1].address, ethers.parseEther("100")); + await token.transfer(accounts[4].address, ethers.parseEther("100")); + }); + + it("should correctly split and withdraw Ether fees", async function () { + // Send Ether to the SwapFeeManager contract + await accounts[1].sendTransaction({ + to: swapFeeManager.target, + value: ethers.parseEther("1"), + }); + + const managerBalance = await ethers.provider.getBalance(swapFeeManager.target); + expect(managerBalance).to.equal(ethers.parseEther("1")); + + await swapFeeManager.connect(accounts[0]).splitAndWithdraw().should.be.fulfilled; + + const dexFeeBalance = await ethers.provider.getBalance(accounts[2].address); + const burnFeeBalance = await ethers.provider.getBalance(accounts[3].address); + + expect(dexFeeBalance).to.equal(ethers.parseEther("0.75")); + expect(burnFeeBalance).to.equal(ethers.parseEther("0.25")); + + // Ensure the fee manager contract's Ether balance is now zero + const managerBalanceAfter = await ethers.provider.getBalance(swapFeeManager.target); + expect(managerBalanceAfter).to.equal(ethers.parseEther("0")); + }); + + it("should correctly split and withdraw ERC20 token fees", async function () { + // Approve and transfer tokens to the SwapFeeManager contract + await token.connect(accounts[1]).approve(swapFeeManager.target, ethers.parseEther("1")); + await token.connect(accounts[1]).transfer(swapFeeManager.target, ethers.parseEther("1")); + + const managerTokenBalance = await token.balanceOf(swapFeeManager.target); + expect(managerTokenBalance).to.equal(ethers.parseEther("1")); + + await swapFeeManager.connect(accounts[0]).splitAndWithdrawToken(token.target).should.be.fulfilled; + + const dexFeeTokenBalance = await token.balanceOf(accounts[2].address); + const burnFeeTokenBalance = await token.balanceOf(accounts[3].address); + + expect(dexFeeTokenBalance).to.equal(ethers.parseEther("0.75")); + expect(burnFeeTokenBalance).to.equal(ethers.parseEther("0.25")); + + // Ensure the fee manager contract's token balance is now zero + const managerTokenBalanceAfter = await token.balanceOf(swapFeeManager.target); + expect(managerTokenBalanceAfter).to.equal(ethers.parseEther("0")); + }); + + it("should allow non-owner to split and withdraw Ether fees", async function () { + await accounts[4].sendTransaction({ + to: swapFeeManager.target, + value: ethers.parseEther("1"), + }); + + // Attempt to call splitAndWithdraw as a non-owner + await swapFeeManager.connect(accounts[4]).splitAndWithdraw().should.be.fulfilled; + }); + + it("should allow non-owner to split and withdraw ERC20 token fees", async function () { + await token.connect(accounts[4]).approve(swapFeeManager.target, ethers.parseEther("1")); + await token.connect(accounts[4]).transfer(swapFeeManager.target, ethers.parseEther("1")); + + // Attempt to call splitAndWithdrawToken as a non-owner + await swapFeeManager.connect(accounts[4]).splitAndWithdrawToken(token.target).should.be.fulfilled; + }); +}); diff --git a/truffle.js b/truffle.js deleted file mode 100644 index 8e18127..0000000 --- a/truffle.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - networks: { - development: { - host: "rpc", - port: 8545, - network_id: "*" // Match any network id - } - }, -}; diff --git a/workspace.Dockerfile b/workspace.Dockerfile index d1878b7..f851c71 100644 --- a/workspace.Dockerfile +++ b/workspace.Dockerfile @@ -1,7 +1,6 @@ -FROM mhart/alpine-node:11 +FROM node:20-bullseye-slim -RUN apk update && apk upgrade && apk add git && apk add python && apk add make && apk add g++ - -RUN npm i -g truffle@5.0.0 VOLUME /usr/src/workspace WORKDIR /usr/src/workspace + +RUN yarn install diff --git a/yarn.lock b/yarn.lock index 7a82def..cee01a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,951 +2,1288 @@ # yarn lockfile v1 -"@types/node@^10.3.2": - version "10.11.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.11.2.tgz#59508b88ce90fe2742f7b8414c6f5db3e359570d" +"@adraffy/ens-normalize@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + +"@ethereumjs/rlp@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-5.0.2.tgz#c89bd82f2f3bec248ab2d517ae25f5bbc4aac842" + integrity sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA== + +"@ethereumjs/util@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-9.1.0.tgz#75e3898a3116d21c135fa9e29886565609129bce" + integrity sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog== + dependencies: + "@ethereumjs/rlp" "^5.0.2" + ethereum-cryptography "^2.2.1" + +"@ethersproject/abi@^5.1.2": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.1.tgz#ab57818d9aefee919c5721d28cd31fd95eff413d" + integrity sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q== + dependencies: + "@ethersproject/bignumber" "^5.6.2" + "@ethersproject/bytes" "^5.6.1" + "@ethersproject/keccak256" "^5.6.1" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/rlp" "^5.6.1" + +"@ethersproject/address@^5.0.2": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983" + integrity sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/bignumber@^5.6.2", "@ethersproject/bignumber@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.8.0.tgz#c381d178f9eeb370923d389284efa19f69efa5d7" + integrity sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + bn.js "^5.2.1" + +"@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.6.1", "@ethersproject/bytes@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.8.0.tgz#9074820e1cac7507a34372cadeb035461463be34" + integrity sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== + dependencies: + "@ethersproject/logger" "^5.8.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": + 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/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@^5.6.1", "@ethersproject/keccak256@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.8.0.tgz#d2123a379567faf2d75d2aaea074ffd4df349e6a" + integrity sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng== + dependencies: + "@ethersproject/bytes" "^5.8.0" + js-sha3 "0.8.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.6.0", "@ethersproject/logger@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.8.0.tgz#f0232968a4f87d29623a0481690a2732662713d6" + integrity sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.6.1", "@ethersproject/rlp@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.8.0.tgz#5a0d49f61bc53e051532a5179472779141451de5" + integrity sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.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/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@fastify/busboy@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" + integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== -accepts@~1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" + "@noble/hashes" "1.3.2" -aes-js@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" +"@noble/curves@1.4.2", "@noble/curves@~1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" -ajv@^5.3.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" +"@noble/curves@~1.8.1": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" + integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" + "@noble/hashes" "1.7.2" -ansi-regex@^2.0.0, ansi-regex@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= +"@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== -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -any-promise@1.3.0, any-promise@^1.0.0, any-promise@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" +"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/hashes@1.7.2", "@noble/hashes@~1.7.1": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" + integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== + +"@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== + +"@nomicfoundation/edr-darwin-arm64@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.22.tgz#2033c1b49461212682fd557002feb41b68677042" + integrity sha512-TpEBSKyMZJEPvYwBPYclC2b+qobKjn1YhVa7aJ1R7RMPy5dJ/PqsrUK5UuUFFybBqoIorru5NTcsyCMWP5T/Fg== + +"@nomicfoundation/edr-darwin-x64@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.22.tgz#f140f5150c80ca6135704831d31875c077309985" + integrity sha512-aK/+m8xUkR4u+czTVGU06nSFVH43AY6XCBoR2YjO8SglAAjCSTWK3WAfVb6FcsriMmKv4PrvoyHLMbMP+fXcGA== + +"@nomicfoundation/edr-linux-arm64-gnu@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.22.tgz#54eb2c8c285067fb5970442a8d8a3252a8b2d23e" + integrity sha512-W5vXMleG14hVzRYGPEwlHLJ6iiQE8Qh63Uj538nAz4YUI6wWSgUOZE7K2Gt1EdujZGnrt7kfDslgJ96n4nKQZw== + +"@nomicfoundation/edr-linux-arm64-musl@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.22.tgz#3862beb688bc2d460d8a2400d4d4e206357dfbc9" + integrity sha512-VDp7EB3iY8MH/fFVcgEzLDGYmtS6j2honNc0RNUCFECKPrdsngGrTG8p+YFxyVjq2m5GEsdyKo4e+BKhaUNPdg== + +"@nomicfoundation/edr-linux-x64-gnu@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.22.tgz#eea4bf9850c8aa37661e8a7625249e43a57014da" + integrity sha512-XL6oA3ymRSQYyvg6hF1KIax6V/9vlWr5gJ8GPHVVODk1a/YfuEEY1osN5Zmo6aztUkSGKwSuac/3Ax7rfDDiSg== + +"@nomicfoundation/edr-linux-x64-musl@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.22.tgz#801cfad4063b831f0bea8d57a34a47e782f44536" + integrity sha512-hmkRIXxWa9P0PwfXOAO6WUw11GyV5gpxcMunqWBTkwZ4QW/hi/CkXmlLo6VHd6ceCwpUNLhCGndBtrOPrNRi4A== + +"@nomicfoundation/edr-win32-x64-msvc@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.22.tgz#303a1d274ccb1a2c4ff1f73b734ebb5c280f1311" + integrity sha512-X7f+7KUMm00trsXAHCHJa+x1fc3QAbk2sBctyOgpET+GLrfCXbxqrccKi7op8f0zTweAVGg1Hsc8SjjC7kwFLw== + +"@nomicfoundation/edr@0.12.0-next.22": + version "0.12.0-next.22" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.12.0-next.22.tgz#f40b373eec002c0170068e39eb90df6037a0d3dc" + integrity sha512-JigYWf2stjpDxSndBsxRoobQHK8kz4SAVaHtTIKQLIHbsBwymE8i120Ejne6Jk+Ndc5CsNINXB8/bK6vLPe9jA== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.12.0-next.22" + "@nomicfoundation/edr-darwin-x64" "0.12.0-next.22" + "@nomicfoundation/edr-linux-arm64-gnu" "0.12.0-next.22" + "@nomicfoundation/edr-linux-arm64-musl" "0.12.0-next.22" + "@nomicfoundation/edr-linux-x64-gnu" "0.12.0-next.22" + "@nomicfoundation/edr-linux-x64-musl" "0.12.0-next.22" + "@nomicfoundation/edr-win32-x64-msvc" "0.12.0-next.22" + +"@nomicfoundation/hardhat-ethers@^3.1.0": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.1.3.tgz#fc0d96083f8f28d82c7c45231f40257da7e67bf9" + integrity sha512-208JcDeVIl+7Wu3MhFUUtiA8TJ7r2Rn3Wr+lSx9PfsDTKkbsAsWPY6N6wQ4mtzDv0/pB9nIbJhkjoHe1EsgNsA== + dependencies: + debug "^4.1.1" + lodash.isequal "^4.5.0" + +"@nomicfoundation/hardhat-ignition-ethers@^0.15.16": + version "0.15.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.17.tgz#7ae75532b459c2da1e2449070d1dcd980cc44907" + integrity sha512-io6Wrp1dUsJ94xEI3pw6qkPfhc9TFA+e6/+o16yQ8pvBTFMjgK5x8wIHKrrIHr9L3bkuTMtmDjyN4doqO2IqFQ== + +"@nomicfoundation/hardhat-ignition@^0.15.16": + version "0.15.16" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.16.tgz#829986d00a5b0233c4fd74fc555a7e7d8f5dad45" + integrity sha512-T0JTnuib7QcpsWkHCPLT7Z6F483EjTdcdjb1e00jqS9zTGCPqinPB66LLtR/duDLdvgoiCVS6K8WxTQkA/xR1Q== + dependencies: + "@nomicfoundation/ignition-core" "^0.15.15" + "@nomicfoundation/ignition-ui" "^0.15.13" + chalk "^4.0.0" + debug "^4.3.2" + fs-extra "^10.0.0" + json5 "^2.2.3" + prompts "^2.4.2" + +"@nomicfoundation/hardhat-verify@^2.0.13": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.1.3.tgz#f61a192a792b39f9978cef1450047deaff3ea052" + integrity sha512-danbGjPp2WBhLkJdQy9/ARM3WQIK+7vwzE0urNem1qZJjh9f54Kf5f1xuQv8DvqewUAkuPxVt/7q4Grz5WjqSg== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + debug "^4.1.1" + lodash.clonedeep "^4.5.0" + picocolors "^1.1.0" + semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" + +"@nomicfoundation/ignition-core@^0.15.15": + version "0.15.15" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ignition-core/-/ignition-core-0.15.15.tgz#2d55c118f93154af7b7e57f0db3d9bfd87ab3544" + integrity sha512-JdKFxYknTfOYtFXMN6iFJ1vALJPednuB+9p9OwGIRdoI6HYSh4ZBzyRURgyXtHFyaJ/SF9lBpsYV9/1zEpcYwg== + dependencies: + "@ethersproject/address" "5.6.1" + "@nomicfoundation/solidity-analyzer" "^0.1.1" + cbor "^9.0.0" + debug "^4.3.2" + ethers "^6.14.0" + fs-extra "^10.0.0" + immer "10.0.2" + lodash "4.17.21" + ndjson "2.0.0" + +"@nomicfoundation/ignition-ui@^0.15.13": + version "0.15.13" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.13.tgz#9cc245d64560f6704ddcd59c5f1b65bf032d0b46" + integrity sha512-HbTszdN1iDHCkUS9hLeooqnLEW2U45FaqFwFEYT8nIno2prFZhG+n68JEERjmfFCB5u0WgbuJwk3CgLoqtSL7Q== + +"@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== -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz#3a9c3b20d51360b20affb8f753e756d553d49557" + integrity sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw== -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" +"@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== -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - dependencies: - safer-buffer "~2.1.0" +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz#74dcfabeb4ca373d95bd0d13692f44fcef133c28" + integrity sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw== -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" +"@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== -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" +"@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== -async-limiter@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz#4af5849a89e5a8f511acc04f28eb5d4460ba2b6a" + integrity sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +"@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== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz#54036808a9a327b2ff84446c130a6687ee702a8e" + integrity sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA== -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" +"@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== -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz#466cda0d6e43691986c944b909fc6dbb8cfc594e" + integrity sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g== -base64-js@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" +"@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== -base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz#2b35826987a6e94444140ac92310baa088ee7f94" + integrity sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg== -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - dependencies: - tweetnacl "^0.14.3" +"@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== -bl@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - -bluebird@^2.9.34: - version "2.11.0" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - -bluebird@^3.5.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a" - -bn.js@4.11.6: - version "4.11.6" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" - -bn.js@4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.6, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== - -body-parser@1.18.2: - version "1.18.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" - dependencies: - bytes "3.0.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.1" - http-errors "~1.6.2" - iconv-lite "0.4.19" - on-finished "~2.3.0" - qs "6.5.1" - raw-body "2.3.2" - type-is "~1.6.15" - -body-parser@^1.16.0: - version "1.18.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" - dependencies: - bytes "3.0.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "~1.6.3" - iconv-lite "0.4.23" - on-finished "~2.3.0" - qs "6.5.2" - raw-body "2.3.3" - type-is "~1.6.16" +"@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== -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" +"@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== -brorand@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz#e6363d13b8709ca66f330562337dbc01ce8bbbd9" + integrity sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA== -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - 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" +"@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" + +"@nomicfoundation/solidity-analyzer@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz#8bcea7d300157bf3a770a851d9f5c5e2db34ac55" + integrity sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.2" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.2" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.2" + +"@openzeppelin/contracts@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.4.0.tgz#177594bdb2d86c71f5d1052fe40cb4edb95fb20f" + integrity sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A== + +"@scure/base@~1.1.0": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157" + integrity sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ== -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" +"@scure/base@~1.1.6": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" +"@scure/base@~1.2.5": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" + integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== -browserify-rsa@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" +"@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: - bn.js "^4.1.0" - randombytes "^2.0.1" + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" -browserify-sha3@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/browserify-sha3/-/browserify-sha3-0.0.1.tgz#3ff34a3006ef15c0fb3567e541b91a2340123d11" +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== dependencies: - js-sha3 "^0.3.1" + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" -browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" +"@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: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" - -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@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" + +"@types/node@22.7.5": + version "22.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" + +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@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== + +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@^8.0.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + string-width "^4.1.0" -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" +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== -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" +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== -buffer-to-arraybuffer@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" +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" -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" +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== -buffer@^3.0.1: - version "3.6.0" - resolved "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb" +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: - base64-js "0.0.8" - ieee754 "^1.1.4" - isarray "^1.0.0" + color-convert "^2.0.1" -buffer@^5.0.5: - version "5.2.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" +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: - base64-js "^1.0.2" - ieee754 "^1.1.4" + normalize-path "^3.0.0" + picomatch "^2.0.4" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +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== -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +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== -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" - dependencies: - check-error "^1.0.2" +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== -chai@^4.1.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - pathval "^1.1.0" - type-detect "^4.0.5" + possible-typed-array-names "^1.0.0" -check-error@^1.0.2: +balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - -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" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -cli-color@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.3.0.tgz#cd2ec212efbd1a0eeb5b017f17d4e2d15e91420f" - dependencies: - ansi-regex "^2.1.1" - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - memoizee "^0.4.14" - timers-ext "^0.1.5" +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== -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" +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== -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +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== -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" -combined-stream@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" +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: - delayed-stream "~1.0.0" + balanced-match "^1.0.0" + concat-map "0.0.1" -combined-stream@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" +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: - delayed-stream "~1.0.0" - -commander@^2.11.0, commander@^2.8.1: - version "2.18.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + balanced-match "^1.0.0" -commander@~2.8.1: - version "2.8.1" - resolved "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" +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: - graceful-readlink ">= 1.0.0" + fill-range "^7.0.1" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" +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== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" +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== -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +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== -cookiejar@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -core-util-is@1.0.2, core-util-is@~1.0.0: +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" -cors@^2.8.1: - version "2.8.4" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686" +call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: - object-assign "^4" - vary "^1" + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" -create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" +call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== dependencies: - bn.js "^4.1.0" - elliptic "^6.0.0" + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" -create-hash@^1.1.0, create-hash@^1.1.2: - version "1.2.0" - resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" +camelcase@^6.0.0, camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" + nofilter "^3.1.0" -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: - version "1.1.7" - resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - 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" - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-browserify@3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" +cbor@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.2.tgz#536b4f2d544411e70ec2b19a2453f10f83cd9fdb" + integrity sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ== dependencies: - es5-ext "^0.10.9" + nofilter "^3.1.0" -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" +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: - assert-plus "^1.0.0" + check-error "^1.0.2" -debug@2.6.9, debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" +chai@^4.3.10: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== dependencies: - ms "2.0.0" + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + +chalk@^4.0.0, chalk@^4.1.0: + 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" + +check-error@^1.0.2, check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + +chokidar@3.5.3: + 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" -debug@^3.0.1: - version "3.2.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.5.tgz#c2418fbfd7a29f4d4f70ff4cea604d4b64c46407" +chokidar@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== dependencies: - ms "^2.1.1" + readdirp "^4.0.1" -decamelize@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +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== -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" +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== -decompress-response@^3.2.0, decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - dependencies: - mimic-response "^1.0.0" +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" +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: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" +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: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" + color-name "~1.1.4" -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - dependencies: - type-detect "^4.0.0" +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== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" +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== -depd@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== -depd@~1.1.1, depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" +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== -des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" +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== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +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== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" +debug@4, debug@4.3.4, debug@^4.1.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + ms "2.1.2" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" +debug@^4.3.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" + ms "^2.1.3" -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +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== -elliptic@6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - inherits "^2.0.1" + type-detect "^4.0.0" -elliptic@^6.0.0, elliptic@^6.4.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" +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== -end-of-stream@^1.0.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - dependencies: - once "^1.4.0" +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== -es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2: - version "0.10.46" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572" - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - next-tick "1" +dotenv@^16.4.5: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== -es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" -es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" +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: - d "1" - es5-ext "~0.10.14" + 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" -es6-weak-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" +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== -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - -eth-ens-namehash@2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" - dependencies: - idna-uts46-hx "^2.3.1" - js-sha3 "^0.5.7" - -eth-lib@0.1.27, eth-lib@^0.1.26: - version "0.1.27" - resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - keccakjs "^0.2.1" - nano-json-stream-parser "^0.1.2" - servify "^0.1.12" - ws "^3.0.0" - xhr-request-promise "^0.1.2" - -eth-lib@0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - xhr-request-promise "^0.1.2" - -ethers@4.0.0-beta.1: - version "4.0.0-beta.1" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.1.tgz#0648268b83e0e91a961b1af971c662cdf8cbab6d" - dependencies: - "@types/node" "^10.3.2" - aes-js "3.0.0" - bn.js "^4.4.0" - elliptic "6.3.3" - hash.js "1.1.3" - js-sha3 "0.5.7" - scrypt-js "2.0.3" - setimmediate "1.0.4" - uuid "2.0.1" - xmlhttprequest "1.8.0" - -ethjs-unit@0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" - dependencies: - bn.js "4.11.6" - number-to-bn "1.7.0" - -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" +enquirer@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== dependencies: - d "1" - es5-ext "~0.10.14" + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" -eventemitter3@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.1.1.tgz#47786bdaa087caf7b1b75e73abc5c7d540158cd0" +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== -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -express@^4.14.0: - version "4.16.3" - resolved "https://registry.npmjs.org/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" - dependencies: - accepts "~1.3.5" - array-flatten "1.1.1" - body-parser "1.18.2" - content-disposition "0.5.2" - content-type "~1.0.4" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.1.1" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" - path-to-regexp "0.1.7" - proxy-addr "~2.0.3" - qs "6.5.1" - range-parser "~1.2.0" - safe-buffer "5.1.1" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" - utils-merge "1.0.1" - vary "~1.1.2" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== -extsprintf@1.3.0: +es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - dependencies: - pend "~1.2.0" - -file-type@^3.8.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" - -file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" - -finalhandler@1.1.1: +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" - unpipe "~1.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 sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: - locate-path "^2.0.0" + es-errors "^1.3.0" -for-each@^0.3.2: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - dependencies: - is-callable "^1.1.3" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" +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== -form-data@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" +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" + +ethereum-cryptography@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== + dependencies: + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + +ethers@^6.14.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.16.0.tgz#fff9b4f05d7a359c774ad6e91085a800f7fccf65" + integrity sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "22.7.5" + aes-js "4.0.0-beta.5" + tslib "2.7.0" + ws "8.17.1" + +fast-deep-equal@^3.1.3: + 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-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + +fdir@^6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689" + integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ== + +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-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== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +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.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + +for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== dependencies: - asynckit "^0.4.0" - combined-stream "1.0.6" - mime-types "^2.1.12" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + is-callable "^1.2.7" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" +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== -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" +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@^2.0.0, fs-extra@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35" +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" -fs-extra@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" +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-promise@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-2.0.3.tgz#f64e4f854bcf689aa8bddcba268916db3db46854" - dependencies: - any-promise "^1.3.0" - fs-extra "^2.0.0" - mz "^2.6.0" - thenify-all "^1.6.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fstream@^1.0.12, fstream@^1.0.8: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -ganache-cli@^6.2.5: - version "6.2.5" - resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.2.5.tgz#efda5115fa3a0c62d7f5729fdd78da70ca55b1ad" - integrity sha512-E4SP8QNeuc2N/ojFoCK+08OYHX8yrtGeFtipZmJPPTQ6U8Hmq3JcbXZDxQfChPQUY5mtbRSwptJa4EtiQyJjAQ== - dependencies: - bn.js "4.11.8" - source-map-support "0.5.9" - yargs "11.1.0" +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== +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" +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" +get-intrinsic@^1.2.4, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" +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: - assert-plus "^1.0.0" + is-glob "^4.0.1" -glob@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" +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" @@ -955,774 +1292,611 @@ glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3: - version "7.1.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.5.tgz#6714c69bee20f3c3e64c4dd905553e532b40cdc0" - integrity sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ== - 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" - -global@~4.3.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" - dependencies: - min-document "^2.19.0" - process "~0.5.1" - -got@7.1.0, got@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, 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== + +hardhat@^2.26.0: + version "2.28.4" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.28.4.tgz#d61ede03ba9637756173f77cfbea32cedc26ca1b" + integrity sha512-iQC4WNWjWMz7cVVFqzEBNisUQ/EEEJrWysJ2hRAMTnfXJx6Y11UXdmtz4dHIzvGL0z27XCCaJrcApDPH0KaZEg== + dependencies: + "@ethereumjs/util" "^9.1.0" + "@ethersproject/abi" "^5.1.2" + "@nomicfoundation/edr" "0.12.0-next.22" + "@nomicfoundation/solidity-analyzer" "^0.1.0" + "@sentry/node" "^5.18.1" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + boxen "^5.1.2" + chokidar "^4.0.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^1.0.3" + find-up "^5.0.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + json-stream-stringify "^3.1.4" + keccak "^3.0.2" + lodash "^4.17.11" + micro-eth-signer "^0.14.0" + mnemonist "^0.38.0" + mocha "^10.0.0" + p-map "^4.0.0" + picocolors "^1.1.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + solc "0.8.26" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + tinyglobby "^0.2.6" + tsort "0.0.1" + undici "^5.14.0" + uuid "^8.3.2" + ws "^7.4.6" + +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== -har-validator@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - ajv "^5.3.0" - har-schema "^2.0.0" + es-define-property "^1.0.0" -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - has-symbol-support-x "^1.4.1" + has-symbols "^1.0.3" -hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" +hash-base@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.2.tgz#79d72def7611c3f6e3c3b5730652638001b10a74" + integrity sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg== dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + inherits "^2.0.4" + readable-stream "^2.3.8" + safe-buffer "^5.2.1" + to-buffer "^1.2.1" -hash.js@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" - minimalistic-assert "^1.0.0" + minimalistic-assert "^1.0.1" -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" + function-bind "^1.1.2" + +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== -hmac-drbg@^1.0.0: +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-errors@1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" - dependencies: - depd "1.1.1" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - -http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" +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 "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" -http-https@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" +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: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + agent-base "6" + debug "4" -iconv-lite@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - -iconv-lite@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" +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" -idna-uts46-hx@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" - dependencies: - punycode "2.1.0" +immer@10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.2.tgz#11636c5b77acf529e059582d76faf338beb56141" + integrity sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA== -ieee754@^1.1.4: - version "1.1.12" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" +immutable@^4.0.0-rc.12: + version "4.3.5" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0" + integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== + +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 sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: +inherits@2, inherits@2.0.4, 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== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - -ipaddr.js@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" - -is-callable@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= +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: - number-is-nan "^1.0.0" - -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 sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" - -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" - -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" - -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - -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" + fp-ts "^1.0.0" -is-promise@^2.1: +is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-typedarray@^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" - -isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - -js-sha3@0.5.7, js-sha3@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" - -js-sha3@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -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" - -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - 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" - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -keccakjs@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/keccakjs/-/keccakjs-0.2.1.tgz#1d633af907ef305bbf9f2fa616d56c44561dfa4d" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: - browserify-sha3 "^0.0.1" - sha3 "^1.1.0" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.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 sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -lodash@^4.13.1: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-queue@0.1: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - dependencies: - es5-ext "~0.10.2" - -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - dependencies: - pify "^3.0.0" - -md5.js@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - dependencies: - mimic-fn "^1.0.0" - -memoizee@^0.4.14: - version "0.4.14" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" - dependencies: - d "1" - es5-ext "^0.10.45" - es6-weak-map "^2.0.2" - event-emitter "^0.3.5" - is-promise "^2.1" - lru-queue "0.1" - next-tick "1" - timers-ext "^0.1.5" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@~1.36.0: - version "1.36.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" - -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.18, mime-types@~2.1.19: - version "2.1.20" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" - dependencies: - mime-db "~1.36.0" - -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - dependencies: - dom-walk "^0.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - -minimalistic-crypto-utils@^1.0.0, 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" - -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" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -mkdirp-promise@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" - dependencies: - mkdirp "*" - -mkdirp@*, "mkdirp@>=0.5 0": - version "0.5.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -mock-fs@^4.1.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.7.0.tgz#9f17e219cacb8094f4010e0a8c38589e2b33c299" - -mout@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/mout/-/mout-0.11.1.tgz#ba3611df5f0e5b1ffbfd01166b8f02d1f5fa2b99" + binary-extensions "^2.0.0" -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +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== -ms@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -mz@^2.6.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" +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-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: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" + is-extglob "^2.1.1" -nan@2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" +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== -nan@^2.0.8, nan@^2.3.3: - version "2.11.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" +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== -nano-json-stream-parser@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" +is-typed-array@^1.1.14: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +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== -next-tick@1: +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/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= +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-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: - path-key "^2.0.0" + argparse "^2.0.1" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +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== -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" - dependencies: - bn.js "4.11.6" - strip-hex-prefix "1.0.0" +json-stream-stringify@^3.1.4: + version "3.1.6" + resolved "https://registry.yarnpkg.com/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz#ebe32193876fb99d4ec9f612389a8d8e2b5d54d4" + integrity sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" +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== -object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -oboe@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.3.tgz#2b4865dbd46be81225713f4e9bfe4bcf4f680a4f" - dependencies: - http-https "^1.0.0" +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" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" +jsonfile@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== dependencies: - ee-first "1.1.1" + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= +keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +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.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash@4.17.21, lodash@^4.17.11: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +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: - wrappy "1" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" -"openzeppelin-solidity@https://github.com/OpenZeppelin/openzeppelin-solidity.git#release-v2.1.0-solc-0.5": - version "2.1.0-rc.1" - resolved "https://github.com/OpenZeppelin/openzeppelin-solidity.git#4b8fcbcee16437d8a29bc0e5bb328b2a1547f996" - -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" + get-func-name "^2.0.1" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" +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== -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== -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" +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== -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= +micro-eth-signer@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz#8aa1fe997d98d6bdf42f2071cef7eb01a66ecb22" + integrity sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw== dependencies: - p-limit "^1.1.0" + "@noble/curves" "~1.8.1" + "@noble/hashes" "~1.7.1" + micro-packed "~0.7.2" -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" +micro-packed@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/micro-packed/-/micro-packed-0.7.3.tgz#59e96b139dffeda22705c7a041476f24cabb12b6" + integrity sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg== dependencies: - p-finally "^1.0.0" + "@scure/base" "~1.2.5" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= +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== -parse-asn1@^5.0.0: - version "5.1.1" - resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" - dependencies: - asn1.js "^4.0.0" - browserify-aes "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" +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== -parse-headers@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536" +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: - for-each "^0.3.2" - trim "0.0.1" - -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + brace-expansion "^2.0.1" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -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 sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +minimatch@^3.0.4: + 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" -path-key@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +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@^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" + +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== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" +ms@2.1.3, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" +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== -pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" +ndjson@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ndjson/-/ndjson-2.0.0.tgz#320ac86f6fe53f5681897349b86ac6f43bfa3a19" + integrity sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ== 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" + json-stringify-safe "^5.0.1" + minimist "^1.2.5" + readable-stream "^3.6.0" + split2 "^3.0.0" + through2 "^4.0.0" -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" +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== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" +node-gyp-build@^4.2.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== -pify@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -pinkie@^2.0.0: +obliterator@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" +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" -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +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== -process@~0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" +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" -proxy-addr@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" +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: - forwarded "~0.1.2" - ipaddr.js "1.8.0" + p-limit "^3.0.2" -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= +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" -psl@^1.1.24: - version "1.1.29" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" +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== -public-encrypt@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" +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== -punycode@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -qs@6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -qs@6.5.2, qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" +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== + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: - safe-buffer "^5.1.0" + kleur "^3.0.3" + sisteransi "^1.0.5" -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" +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: - randombytes "^2.0.5" safe-buffer "^5.1.0" -randomhex@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" - -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -raw-body@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" +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.0.0" - http-errors "1.6.2" - iconv-lite "0.4.19" + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" +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: - bytes "3.0.0" - http-errors "1.6.3" - iconv-lite "0.4.23" - unpipe "1.0.0" + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" -readable-stream@^2.3.0, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" +readable-stream@^2.3.8: + 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" @@ -1732,225 +1906,123 @@ readable-stream@^2.3.0, readable-stream@^2.3.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" -request-promise-core@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" - dependencies: - lodash "^4.13.1" +readdirp@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.1.tgz#bd115327129672dc47f87408f05df9bd9ca3ef55" + integrity sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw== -request-promise-native@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" - dependencies: - request-promise-core "1.1.1" - stealthy-require "^1.1.0" - tough-cookie ">=2.3.3" - -request@^2.79.0, request@^2.83.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - 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.0" - 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.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" +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" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +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== -rimraf@2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== +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: - glob "^7.1.3" + path-parse "^1.0.6" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" +ripemd160@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.3.tgz#9be54e4ba5e3559c8eee06a25cd7648bbccdf5a8" + integrity sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA== dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" + hash-base "^3.1.2" + inherits "^2.0.4" -safe-buffer@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@^5.1.0, safe-buffer@^5.2.1, 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.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +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== -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scrypt-js@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" - -scrypt.js@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada" - dependencies: - scrypt "^6.0.2" - scryptsy "^1.2.1" - -scrypt@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" - dependencies: - nan "^2.0.8" - -scryptsy@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" - dependencies: - pbkdf2 "^3.0.3" - -seek-bzip@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" - dependencies: - commander "~2.8.1" - -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -serve-static@1.13.2: - version "1.13.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" - -servify@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" - dependencies: - body-parser "^1.16.0" - cors "^2.8.1" - express "^4.14.0" - request "^2.79.0" - xhr "^2.3.3" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -setimmediate@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" +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: - inherits "^2.0.1" - safe-buffer "^5.0.1" + randombytes "^2.1.0" -sha3@^1.1.0: +set-function-length@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.2.tgz#a66c5098de4c25bc88336ec8b4817d005bca7ba9" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: - nan "2.10.0" + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" -shebang-command@^1.2.0: +setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -simple-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -simple-get@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" - dependencies: - decompress-response "^3.3.0" - once "^1.3.1" - simple-concat "^1.0.0" - -sol-merger@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/sol-merger/-/sol-merger-0.1.2.tgz#1f12500f42d427dc0ec8e4c113392acd8a6f62d9" - dependencies: - bluebird "^3.5.0" - cli-color "^1.2.0" - commander "^2.11.0" - debug "^3.0.1" - fs-extra "^4.0.2" - glob "^7.1.2" +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -source-map-support@0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" - integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== +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" + +solc@0.8.26: + version "0.8.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.26.tgz#afc78078953f6ab3e727c338a2fefcd80dd5b01a" + integrity sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g== + dependencies: + command-exists "^1.2.8" + commander "^8.1.0" + follow-redirects "^1.12.1" + js-sha3 "0.8.0" + memorystream "^0.3.1" + semver "^5.5.0" + tmp "0.0.33" + +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" @@ -1958,612 +2030,299 @@ source-map-support@0.5.9: source-map@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sshpk@^1.7.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" +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: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - safer-buffer "^2.0.2" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + readable-stream "^3.0.0" -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - -stealthy-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" +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" -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" +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== -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, 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== dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" -string-width@^2.0.0, 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== +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: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + 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@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= +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 "^2.0.0" + ansi-regex "^5.0.1" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" +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== -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" +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: - is-natural-number "^4.0.1" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + has-flag "^4.0.0" -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" - dependencies: - is-hex-prefixed "1.0.0" - -swarm-js@0.1.37: - version "0.1.37" - resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.37.tgz#27d485317a340bbeec40292af783cc10acfa4663" - dependencies: - bluebird "^3.5.0" - buffer "^5.0.5" - decompress "^4.0.0" - eth-lib "^0.1.26" - fs-extra "^2.1.2" - fs-promise "^2.0.0" - got "^7.1.0" - mime-types "^2.1.16" - mkdirp-promise "^5.0.1" - mock-fs "^4.1.0" - setimmediate "^1.0.5" - tar.gz "^1.0.5" - xhr-request-promise "^0.1.2" - -tar-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" - dependencies: - bl "^1.0.0" - buffer-alloc "^1.2.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.1" - xtend "^4.0.0" - -tar.gz@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/tar.gz/-/tar.gz-1.0.7.tgz#577ef2c595faaa73452ef0415fed41113212257b" +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: - bluebird "^2.9.34" - commander "^2.8.1" - fstream "^1.0.8" - mout "^0.11.0" - tar "^2.1.1" + has-flag "^4.0.0" -tar@^2.1.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" - integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== +table@^6.8.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" + integrity sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== dependencies: - block-stream "*" - fstream "^1.0.12" - inherits "2" + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" -thenify-all@^1.0.0, thenify-all@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" +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: - thenify ">= 3.1.0 < 4" + readable-stream "3" -"thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" +tinyglobby@^0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" + integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== dependencies: - any-promise "^1.0.0" - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -timed-out@^4.0.0, timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + fdir "^6.4.2" + picomatch "^4.0.2" -timers-ext@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.5.tgz#77147dd4e76b660c2abb8785db96574cbbd12922" +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: - es5-ext "~0.10.14" - next-tick "1" + os-tmpdir "~1.0.2" -to-buffer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" +to-buffer@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.2.2.tgz#ffe59ef7522ada0a2d1cb5dfe03bb8abc3cdc133" + integrity sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw== + dependencies: + isarray "^2.0.5" + safe-buffer "^5.2.1" + typed-array-buffer "^1.0.3" -tough-cookie@>=2.3.3, tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" +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: - psl "^1.1.24" - punycode "^1.4.1" + is-number "^7.0.0" -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" +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== -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" +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== + +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.8: 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-is@~1.6.15, type-is@~1.6.16: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -typedarray-to-buffer@^3.1.2: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - dependencies: - is-typedarray "^1.0.0" +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== -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" +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== -unbzip2-stream@^1.0.9: - version "1.3.0" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.0.tgz#745ad5745bc4d8f1ac2eb6fc707cfa51d52ab215" +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== dependencies: - buffer "^3.0.1" - through "^2.3.6" + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" -underscore@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +undici@^5.14.0: + version "5.28.2" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.2.tgz#fea200eac65fc7ecaff80a023d1a0543423b4c91" + integrity sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w== + dependencies: + "@fastify/busboy" "^2.0.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== -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - dependencies: - prepend-http "^1.0.1" +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -url-set-query@^1.0.0: +unpipe@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" - -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - -utf8@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.1.tgz#2e01db02f7d8d0944f77104f1609eb0c304cf768" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -util-deprecate@~1.0.1: +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" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - -uuid@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" - -uuid@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - -vary@^1, vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -web3-bzz@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.36.tgz#adb3fe7a70053eb7843e32b106792b01b482ef41" - dependencies: - got "7.1.0" - swarm-js "0.1.37" - underscore "1.8.3" - -web3-core-helpers@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.36.tgz#6f618e80f1a6588d846efbfdc28f92ae0477f8d2" - dependencies: - underscore "1.8.3" - web3-eth-iban "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-core-method@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.36.tgz#855c0365ae7d0ead394d973ea9e28828602900e0" - dependencies: - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.36" - web3-core-promievent "1.0.0-beta.36" - web3-core-subscriptions "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-core-promievent@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.36.tgz#3a5127787fff751be6de272722cbc77dc9523fd5" - dependencies: - any-promise "1.3.0" - eventemitter3 "1.1.1" - -web3-core-requestmanager@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.36.tgz#70c8eead84da9ed1cf258e6dde3f137116d0691b" - dependencies: - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.36" - web3-providers-http "1.0.0-beta.36" - web3-providers-ipc "1.0.0-beta.36" - web3-providers-ws "1.0.0-beta.36" - -web3-core-subscriptions@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.36.tgz#20f1f20c85d5b40f1e5a49b070ba977a142621f3" - dependencies: - eventemitter3 "1.1.1" - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.36" - -web3-core@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.36.tgz#86182f2456c2cf1cd6e7654d314e195eac211917" - dependencies: - web3-core-helpers "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-core-requestmanager "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-eth-abi@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.36.tgz#21c0f222701db827a8a269accb9cd18bbd8f70f9" - dependencies: - ethers "4.0.0-beta.1" - underscore "1.8.3" - web3-utils "1.0.0-beta.36" - -web3-eth-accounts@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.36.tgz#8aea37df9b038ef2c6cda608856ffd861b39eeef" - dependencies: - any-promise "1.3.0" - crypto-browserify "3.12.0" - eth-lib "0.2.7" - scrypt.js "0.2.0" - underscore "1.8.3" - uuid "2.0.1" - web3-core "1.0.0-beta.36" - web3-core-helpers "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-eth-contract@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.36.tgz#c0c366c4e4016896142208cee758a2ff2a31be2a" - dependencies: - underscore "1.8.3" - web3-core "1.0.0-beta.36" - web3-core-helpers "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-core-promievent "1.0.0-beta.36" - web3-core-subscriptions "1.0.0-beta.36" - web3-eth-abi "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-eth-ens@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.0.0-beta.36.tgz#c7440b42b597fd74f64bc402f03ad2e832f423d8" - dependencies: - eth-ens-namehash "2.0.8" - underscore "1.8.3" - web3-core "1.0.0-beta.36" - web3-core-helpers "1.0.0-beta.36" - web3-core-promievent "1.0.0-beta.36" - web3-eth-abi "1.0.0-beta.36" - web3-eth-contract "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-eth-iban@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.36.tgz#00cb3aba7a5aeb15d02b07421042e263d7b2e01b" - dependencies: - bn.js "4.11.6" - web3-utils "1.0.0-beta.36" - -web3-eth-personal@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.36.tgz#95545998a8ee377e3bb71e27c8d1a5dc1d7d5a21" - dependencies: - web3-core "1.0.0-beta.36" - web3-core-helpers "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-net "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-eth@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.36.tgz#04a8c748d344c1accaa26d7d5d0eac0da7127f14" - dependencies: - underscore "1.8.3" - web3-core "1.0.0-beta.36" - web3-core-helpers "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-core-subscriptions "1.0.0-beta.36" - web3-eth-abi "1.0.0-beta.36" - web3-eth-accounts "1.0.0-beta.36" - web3-eth-contract "1.0.0-beta.36" - web3-eth-ens "1.0.0-beta.36" - web3-eth-iban "1.0.0-beta.36" - web3-eth-personal "1.0.0-beta.36" - web3-net "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-net@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.36.tgz#396cd35cb40934ed022a1f44a8a642d3908c41eb" - dependencies: - web3-core "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -web3-providers-http@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.36.tgz#c1937a2e64f8db7cd30f166794e37cf0fcca1131" - dependencies: - web3-core-helpers "1.0.0-beta.36" - xhr2-cookies "1.1.0" - -web3-providers-ipc@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.36.tgz#0c78efb4ed6b0305ec830e1e0b785e61217ee605" - dependencies: - oboe "2.1.3" - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.36" - -web3-providers-ws@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.36.tgz#27b74082c7adfa0cb5a65535eb312e49008c97c3" - dependencies: - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.36" - websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" - -web3-shh@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.36.tgz#6ff297594480edefc710d9d287765a0c4a5d5af1" - dependencies: - web3-core "1.0.0-beta.36" - web3-core-method "1.0.0-beta.36" - web3-core-subscriptions "1.0.0-beta.36" - web3-net "1.0.0-beta.36" - -web3-utils@1.0.0-beta.36: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.36.tgz#dc19c9aeec009b1816cc91ef64d7fe9f8ee344c9" - dependencies: - bn.js "4.11.6" - eth-lib "0.1.27" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randomhex "0.1.5" - underscore "1.8.3" - utf8 "2.1.1" - -web3@^1.0.0-beta.27: - version "1.0.0-beta.36" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.36.tgz#2954da9e431124c88396025510d840ba731c8373" - dependencies: - web3-bzz "1.0.0-beta.36" - web3-core "1.0.0-beta.36" - web3-eth "1.0.0-beta.36" - web3-eth-personal "1.0.0-beta.36" - web3-net "1.0.0-beta.36" - web3-shh "1.0.0-beta.36" - web3-utils "1.0.0-beta.36" - -"websocket@git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible": - version "1.0.26" - resolved "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2" - dependencies: - debug "^2.2.0" - nan "^2.3.3" - typedarray-to-buffer "^3.1.2" - yaeti "^0.0.6" - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.9: - 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" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +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== + +which-typed-array@^1.1.16: + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.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@^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 sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -ws@^3.0.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - -xhr-request-promise@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz#343c44d1ee7726b8648069682d0f840c83b4261d" - dependencies: - xhr-request "^1.0.1" - -xhr-request@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" - dependencies: - buffer-to-arraybuffer "^0.0.5" - object-assign "^4.1.1" - query-string "^5.0.1" - simple-get "^2.7.0" - timed-out "^4.0.1" - url-set-query "^1.0.0" - xhr "^2.0.4" - -xhr2-cookies@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" - dependencies: - cookiejar "^2.1.1" - -xhr@^2.0.4, xhr@^2.3.3: - version "2.5.0" - resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" - dependencies: - global "~4.3.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" - -xmlhttprequest@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" - -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yargs-parser@^9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" - integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= - dependencies: - camelcase "^4.1.0" - -yargs@11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" - integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +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== + +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@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@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" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" - -yauzl@^2.4.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +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==