From cec161ad9aed4b3d0eb96ffaee00e75f4e1a1532 Mon Sep 17 00:00:00 2001 From: Dennis <10233439+idea404@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:53:41 +0100 Subject: [PATCH] feat: add more examples --- .github/workflows/tests.yml | 25 +++ .gitignore | 2 + contracts/DefaultAccount.sol | 230 ++++++++++++++++++++++ contracts/DefaultAccountFactory.sol | 36 ++++ contracts/PensionAccount.sol | 239 +++++++++++++++++++++++ contracts/PensionAccountFactory.sol | 42 ++++ deploy/deploy-factory.ts | 8 +- deploy/deploy-multisig.ts | 9 +- hardhat.config.ts | 7 + package.json | 10 + test/main.test.ts | 88 +++++++++ test/utils.ts | 169 ++++++++++++++++ yarn.lock | 289 +++++++++++++++++++++++++++- 13 files changed, 1142 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/tests.yml create mode 100644 contracts/DefaultAccount.sol create mode 100644 contracts/DefaultAccountFactory.sol create mode 100644 contracts/PensionAccount.sol create mode 100644 contracts/PensionAccountFactory.sol create mode 100644 test/main.test.ts create mode 100644 test/utils.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..386ee44 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,25 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + tests: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js LTS + uses: actions/setup-node@v2 + with: + node-version: 'lts/*' + - name: Install Dependencies + run: yarn install + - name: Run Tests + run: yarn test diff --git a/.gitignore b/.gitignore index 2a3c796..e95de1a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules/ artifacts-zk/ cache-zk/ +.env +era_test_node.log diff --git a/contracts/DefaultAccount.sol b/contracts/DefaultAccount.sol new file mode 100644 index 0000000..5b52e69 --- /dev/null +++ b/contracts/DefaultAccount.sol @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IAccount.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractHelper.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/EfficientCall.sol"; +import {BOOTLOADER_FORMAL_ADDRESS, NONCE_HOLDER_SYSTEM_CONTRACT, DEPLOYER_SYSTEM_CONTRACT, INonceHolder} from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; + + +/** + * @author Matter Labs + * @custom:security-contact security@matterlabs.dev + * @notice The default implementation of account. + * @dev The bytecode of the contract is set by default for all addresses for which no other bytecodes are deployed. + * @notice If the caller is not a bootloader always returns empty data on call, just like EOA does. + * @notice If it is delegate called always returns empty data, just like EOA does. + */ +contract DefaultAccount is IAccount { + using TransactionHelper for *; + + /** + * @dev Simulate the behavior of the EOA if the caller is not the bootloader. + * Essentially, for all non-bootloader callers halt the execution with empty return data. + * If all functions will use this modifier AND the contract will implement an empty payable fallback() + * then the contract will be indistinguishable from the EOA when called. + */ + modifier ignoreNonBootloader() { + if (msg.sender != BOOTLOADER_FORMAL_ADDRESS) { + // If function was called outside of the bootloader, behave like an EOA. + assembly { + return(0, 0) + } + } + // Continue execution if called from the bootloader. + _; + } + + /** + * @dev Simulate the behavior of the EOA if it is called via `delegatecall`. + * Thus, the default account on a delegate call behaves the same as EOA on Ethereum. + * If all functions will use this modifier AND the contract will implement an empty payable fallback() + * then the contract will be indistinguishable from the EOA when called. + */ + modifier ignoreInDelegateCall() { + address codeAddress = SystemContractHelper.getCodeAddress(); + if (codeAddress != address(this)) { + // If the function was delegate called, behave like an EOA. + assembly { + return(0, 0) + } + } + + // Continue execution if not delegate called. + _; + } + + /// @notice Validates the transaction & increments nonce. + /// @dev The transaction is considered accepted by the account if + /// the call to this function by the bootloader does not revert + /// and the nonce has been set as used. + /// @param _suggestedSignedHash The suggested hash of the transaction to be signed by the user. + /// This is the hash that is signed by the EOA by default. + /// @param _transaction The transaction structure itself. + /// @dev Besides the params above, it also accepts unused first paramter "_txHash", which + /// is the unique (canonical) hash of the transaction. + function validateTransaction( + bytes32, // _txHash + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) external payable override ignoreNonBootloader ignoreInDelegateCall returns (bytes4 magic) { + magic = _validateTransaction(_suggestedSignedHash, _transaction); + } + + /// @notice Inner method for validating transaction and increasing the nonce + /// @param _suggestedSignedHash The hash of the transaction signed by the EOA + /// @param _transaction The transaction. + function _validateTransaction( + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) internal returns (bytes4 magic) { + // Note, that nonce holder can only be called with "isSystem" flag. + SystemContractsCaller.systemCallWithPropagatedRevert( + uint32(gasleft()), + address(NONCE_HOLDER_SYSTEM_CONTRACT), + 0, + abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_transaction.nonce)) + ); + + // Even though for the transaction types present in the system right now, + // we always provide the suggested signed hash, this should not be + // always expected. In case the bootloader has no clue what the default hash + // is, the bytes32(0) will be supplied. + bytes32 txHash = _suggestedSignedHash != bytes32(0) ? _suggestedSignedHash : _transaction.encodeHash(); + + // The fact there is are enough balance for the account + // should be checked explicitly to prevent user paying for fee for a + // transaction that wouldn't be included on Ethereum. + uint256 totalRequiredBalance = _transaction.totalRequiredBalance(); + require(totalRequiredBalance <= address(this).balance, "Not enough balance for fee + value"); + + if (_isValidSignature(txHash, _transaction.signature)) { + magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC; + } + } + + /// @notice Method called by the bootloader to execute the transaction. + /// @param _transaction The transaction to execute. + /// @dev It also accepts unused _txHash and _suggestedSignedHash parameters: + /// the unique (canonical) hash of the transaction and the suggested signed + /// hash of the transaction. + function executeTransaction( + bytes32, // _txHash + bytes32, // _suggestedSignedHash + Transaction calldata _transaction + ) external payable override ignoreNonBootloader ignoreInDelegateCall { + _execute(_transaction); + } + + /// @notice Method that should be used to initiate a transaction from this account by an external call. + /// @dev The custom account is supposed to implement this method to initiate a transaction on behalf + /// of the account via L1 -> L2 communication. However, the default account can initiate a transaction + /// from L1, so we formally implement the interface method, but it doesn't execute any logic. + /// @param _transaction The transaction to execute. + function executeTransactionFromOutside(Transaction calldata _transaction) external payable override { + // Behave the same as for fallback/receive, just execute nothing, returns nothing + } + + /// @notice Inner method for executing a transaction. + /// @param _transaction The transaction to execute. + function _execute(Transaction calldata _transaction) internal { + address to = address(uint160(_transaction.to)); + uint128 value = Utils.safeCastToU128(_transaction.value); + bytes calldata data = _transaction.data; + uint32 gas = Utils.safeCastToU32(gasleft()); + + // Note, that the deployment method from the deployer contract can only be called with a "systemCall" flag. + bool isSystemCall; + if (to == address(DEPLOYER_SYSTEM_CONTRACT) && data.length >= 4) { + bytes4 selector = bytes4(data[:4]); + // Check that called function is the deployment method, + // the others deployer method is not supposed to be called from the default account. + isSystemCall = + selector == DEPLOYER_SYSTEM_CONTRACT.create.selector || + selector == DEPLOYER_SYSTEM_CONTRACT.create2.selector || + selector == DEPLOYER_SYSTEM_CONTRACT.createAccount.selector || + selector == DEPLOYER_SYSTEM_CONTRACT.create2Account.selector; + } + bool success = EfficientCall.rawCall(gas, to, value, data, isSystemCall); + if (!success) { + EfficientCall.propagateRevert(); + } + } + + /// @notice Validation that the ECDSA signature of the transaction is correct. + /// @param _hash The hash of the transaction to be signed. + /// @param _signature The signature of the transaction. + /// @return EIP1271_SUCCESS_RETURN_VALUE if the signaure is correct. It reverts otherwise. + function _isValidSignature(bytes32 _hash, bytes memory _signature) internal view returns (bool) { + require(_signature.length == 65, "Signature length is incorrect"); + uint8 v; + bytes32 r; + bytes32 s; + // Signature loading code + // we jump 32 (0x20) as the first slot of bytes contains the length + // we jump 65 (0x41) per signature + // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask + assembly { + r := mload(add(_signature, 0x20)) + s := mload(add(_signature, 0x40)) + v := and(mload(add(_signature, 0x41)), 0xff) + } + require(v == 27 || v == 28, "v is neither 27 nor 28"); + + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "Invalid s"); + + address recoveredAddress = ecrecover(_hash, v, r, s); + + return recoveredAddress == address(this) && recoveredAddress != address(0); + } + + /// @notice Method for paying the bootloader for the transaction. + /// @param _transaction The transaction for which the fee is paid. + /// @dev It also accepts unused _txHash and _suggestedSignedHash parameters: + /// the unique (canonical) hash of the transaction and the suggested signed + /// hash of the transaction. + function payForTransaction( + bytes32, // _txHash + bytes32, // _suggestedSignedHash + Transaction calldata _transaction + ) external payable ignoreNonBootloader ignoreInDelegateCall { + bool success = _transaction.payToTheBootloader(); + require(success, "Failed to pay the fee to the operator"); + } + + /// @notice Method, where the user should prepare for the transaction to be + /// paid for by a paymaster. + /// @dev Here, the account should set the allowance for the smart contracts + /// @param _transaction The transaction. + /// @dev It also accepts unused _txHash and _suggestedSignedHash parameters: + /// the unique (canonical) hash of the transaction and the suggested signed + /// hash of the transaction. + function prepareForPaymaster( + bytes32, // _txHash + bytes32, // _suggestedSignedHash + Transaction calldata _transaction + ) external payable ignoreNonBootloader ignoreInDelegateCall { + _transaction.processPaymasterInput(); + } + + fallback() external payable ignoreInDelegateCall { + // fallback of default account shouldn't be called by bootloader under no circumstances + assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS); + + // If the contract is called directly, behave like an EOA + } + + receive() external payable { + // If the contract is called directly, behave like an EOA + } +} \ No newline at end of file diff --git a/contracts/DefaultAccountFactory.sol b/contracts/DefaultAccountFactory.sol new file mode 100644 index 0000000..c726b0e --- /dev/null +++ b/contracts/DefaultAccountFactory.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; + +contract DefaultAccountFactory { + bytes32 public pensionAccountBytecodeHash; + + constructor(bytes32 _pensionAccountBytecodeHash) { + pensionAccountBytecodeHash = _pensionAccountBytecodeHash; + } + + function deployPensionAccount( + bytes32 salt + ) external returns (address pensionAccountAddress) { + (bool success, bytes memory returnData) = SystemContractsCaller + .systemCallWithReturndata( + uint32(gasleft()), + address(DEPLOYER_SYSTEM_CONTRACT), + uint128(0), + abi.encodeCall( + DEPLOYER_SYSTEM_CONTRACT.create2Account, + ( + salt, + pensionAccountBytecodeHash, + abi.encode(), + IContractDeployer.AccountAbstractionVersion.Version1 + ) + ) + ); + require(success, "Deployment failed"); + + (pensionAccountAddress) = abi.decode(returnData, (address)); + } +} diff --git a/contracts/PensionAccount.sol b/contracts/PensionAccount.sol new file mode 100644 index 0000000..79c1d3e --- /dev/null +++ b/contracts/PensionAccount.sol @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IAccount.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractHelper.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/EfficientCall.sol"; +import {BOOTLOADER_FORMAL_ADDRESS, NONCE_HOLDER_SYSTEM_CONTRACT, DEPLOYER_SYSTEM_CONTRACT, INonceHolder} from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; + + +contract PensionAccount is IAccount { + // to get transaction hash + using TransactionHelper for *; + + // Owner of the account + address public owner; + + // Addresses for DEX and tokens + address public dex; + address public DOGE; + address public PEPE; + address public SHIB; + address public BTC; + + // Expiry timestamp + uint256 public expiryTimestamp; + + // Event for swap action + event Swap(address indexed token, uint256 amountToSwap); + + constructor(address _owner, address _dex, address _doge, address _pepe, address _shib, address _btc) { + owner = _owner; + dex = _dex; + DOGE = _doge; + PEPE = _pepe; + SHIB = _shib; + BTC = _btc; + + // Set the expiry timestamp to 3 minutes from now + expiryTimestamp = block.timestamp + 3 minutes; + } + + // Modifier to check if the time lock has expired + modifier afterExpiry() { + require(block.timestamp >= expiryTimestamp, "Action locked until expiry time"); + _; + } + + receive() external payable { + uint256 amountToSwap = msg.value / 4; + emit Swap(DOGE, amountToSwap); + emit Swap(PEPE, amountToSwap); + emit Swap(SHIB, amountToSwap); + emit Swap(BTC, amountToSwap); + } + + + // Override the executeTransaction function to include the time lock + function executeTransaction( + bytes32, // _txHash + bytes32, // _suggestedSignedHash + Transaction calldata _transaction + ) external payable ignoreNonBootloader ignoreInDelegateCall afterExpiry { + _execute(_transaction); + } + + // Override the payForTransaction function to include the time lock + function payForTransaction( + bytes32, // _txHash + bytes32, // _suggestedSignedHash + Transaction calldata _transaction + ) external payable ignoreNonBootloader ignoreInDelegateCall afterExpiry { + bool success = _transaction.payToTheBootloader(); + require(success, "Failed to pay the fee to the operator"); + } + + // Override the prepareForPaymaster function to include the time lock + function prepareForPaymaster( + bytes32, // _txHash + bytes32, // _suggestedSignedHash + Transaction calldata _transaction + ) external payable ignoreNonBootloader ignoreInDelegateCall afterExpiry { + _transaction.processPaymasterInput(); + } + + // ================================================================================================================= + // START DEFAULT ACCOUNT CODE + // ================================================================================================================= + + /** + * @dev Simulate the behavior of the EOA if the caller is not the bootloader. + * Essentially, for all non-bootloader callers halt the execution with empty return data. + * If all functions will use this modifier AND the contract will implement an empty payable fallback() + * then the contract will be indistinguishable from the EOA when called. + */ + modifier ignoreNonBootloader() { + if (msg.sender != BOOTLOADER_FORMAL_ADDRESS) { + // If function was called outside of the bootloader, behave like an EOA. + assembly { + return(0, 0) + } + } + // Continue execution if called from the bootloader. + _; + } + + /** + * @dev Simulate the behavior of the EOA if it is called via `delegatecall`. + * Thus, the default account on a delegate call behaves the same as EOA on Ethereum. + * If all functions will use this modifier AND the contract will implement an empty payable fallback() + * then the contract will be indistinguishable from the EOA when called. + */ + modifier ignoreInDelegateCall() { + address codeAddress = SystemContractHelper.getCodeAddress(); + if (codeAddress != address(this)) { + // If the function was delegate called, behave like an EOA. + assembly { + return(0, 0) + } + } + + // Continue execution if not delegate called. + _; + } + + /// @notice Validates the transaction & increments nonce. + /// @dev The transaction is considered accepted by the account if + /// the call to this function by the bootloader does not revert + /// and the nonce has been set as used. + /// @param _suggestedSignedHash The suggested hash of the transaction to be signed by the user. + /// This is the hash that is signed by the EOA by default. + /// @param _transaction The transaction structure itself. + /// @dev Besides the params above, it also accepts unused first paramter "_txHash", which + /// is the unique (canonical) hash of the transaction. + function validateTransaction( + bytes32, // _txHash + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) external payable override ignoreNonBootloader ignoreInDelegateCall returns (bytes4 magic) { + magic = _validateTransaction(_suggestedSignedHash, _transaction); + } + + /// @notice Inner method for validating transaction and increasing the nonce + /// @param _suggestedSignedHash The hash of the transaction signed by the EOA + /// @param _transaction The transaction. + function _validateTransaction( + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) internal returns (bytes4 magic) { + // Note, that nonce holder can only be called with "isSystem" flag. + SystemContractsCaller.systemCallWithPropagatedRevert( + uint32(gasleft()), + address(NONCE_HOLDER_SYSTEM_CONTRACT), + 0, + abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_transaction.nonce)) + ); + + // Even though for the transaction types present in the system right now, + // we always provide the suggested signed hash, this should not be + // always expected. In case the bootloader has no clue what the default hash + // is, the bytes32(0) will be supplied. + bytes32 txHash = _suggestedSignedHash != bytes32(0) ? _suggestedSignedHash : _transaction.encodeHash(); + + // The fact there is are enough balance for the account + // should be checked explicitly to prevent user paying for fee for a + // transaction that wouldn't be included on Ethereum. + uint256 totalRequiredBalance = _transaction.totalRequiredBalance(); + require(totalRequiredBalance <= address(this).balance, "Not enough balance for fee + value"); + + if (_isValidSignature(txHash, _transaction.signature)) { + magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC; + } + } + + /// @notice Method that should be used to initiate a transaction from this account by an external call. + /// @dev The custom account is supposed to implement this method to initiate a transaction on behalf + /// of the account via L1 -> L2 communication. However, the default account can initiate a transaction + /// from L1, so we formally implement the interface method, but it doesn't execute any logic. + /// @param _transaction The transaction to execute. + function executeTransactionFromOutside(Transaction calldata _transaction) external payable override { + // Behave the same as for fallback/receive, just execute nothing, returns nothing + } + + /// @notice Inner method for executing a transaction. + /// @param _transaction The transaction to execute. + function _execute(Transaction calldata _transaction) internal { + address to = address(uint160(_transaction.to)); + uint128 value = Utils.safeCastToU128(_transaction.value); + bytes calldata data = _transaction.data; + uint32 gas = Utils.safeCastToU32(gasleft()); + + // Note, that the deployment method from the deployer contract can only be called with a "systemCall" flag. + bool isSystemCall; + if (to == address(DEPLOYER_SYSTEM_CONTRACT) && data.length >= 4) { + bytes4 selector = bytes4(data[:4]); + // Check that called function is the deployment method, + // the others deployer method is not supposed to be called from the default account. + isSystemCall = + selector == DEPLOYER_SYSTEM_CONTRACT.create.selector || + selector == DEPLOYER_SYSTEM_CONTRACT.create2.selector || + selector == DEPLOYER_SYSTEM_CONTRACT.createAccount.selector || + selector == DEPLOYER_SYSTEM_CONTRACT.create2Account.selector; + } + bool success = EfficientCall.rawCall(gas, to, value, data, isSystemCall); + if (!success) { + EfficientCall.propagateRevert(); + } + } + + /// @notice Validation that the ECDSA signature of the transaction is correct. + /// @param _hash The hash of the transaction to be signed. + /// @param _signature The signature of the transaction. + /// @return EIP1271_SUCCESS_RETURN_VALUE if the signaure is correct. It reverts otherwise. + function _isValidSignature(bytes32 _hash, bytes memory _signature) internal view returns (bool) { + require(_signature.length == 65, "Signature length is incorrect"); + uint8 v; + bytes32 r; + bytes32 s; + assembly { + r := mload(add(_signature, 0x20)) + s := mload(add(_signature, 0x40)) + v := and(mload(add(_signature, 0x41)), 0xff) + } + require(v == 27 || v == 28, "v is neither 27 nor 28"); + + require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "Invalid s"); + + address recoveredAddress = ecrecover(_hash, v, r, s); + + return recoveredAddress == owner && recoveredAddress != address(0); + } + + fallback() external payable ignoreInDelegateCall { + // fallback of default account shouldn't be called by bootloader under no circumstances + assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS); + + // If the contract is called directly, behave like an EOA + } +} diff --git a/contracts/PensionAccountFactory.sol b/contracts/PensionAccountFactory.sol new file mode 100644 index 0000000..1dbb783 --- /dev/null +++ b/contracts/PensionAccountFactory.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; +import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; + +contract PensionAccountFactory { + bytes32 public pensionAccountBytecodeHash; + + constructor(bytes32 _pensionAccountBytecodeHash) { + pensionAccountBytecodeHash = _pensionAccountBytecodeHash; + } + + function deployPensionAccount( + bytes32 salt, + address owner, + address dex, + address doge, + address pepe, + address shib, + address btc + ) external returns (address pensionAccountAddress) { + (bool success, bytes memory returnData) = SystemContractsCaller + .systemCallWithReturndata( + uint32(gasleft()), + address(DEPLOYER_SYSTEM_CONTRACT), + uint128(0), + abi.encodeCall( + DEPLOYER_SYSTEM_CONTRACT.create2Account, + ( + salt, + pensionAccountBytecodeHash, + abi.encode(owner, dex, doge, pepe, shib, btc), + IContractDeployer.AccountAbstractionVersion.Version1 + ) + ) + ); + require(success, "Deployment failed"); + + (pensionAccountAddress) = abi.decode(returnData, (address)); + } +} diff --git a/deploy/deploy-factory.ts b/deploy/deploy-factory.ts index 563e20d..fb95dc9 100644 --- a/deploy/deploy-factory.ts +++ b/deploy/deploy-factory.ts @@ -1,11 +1,15 @@ import { utils, Wallet } from "zksync-web3"; -import * as ethers from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; +import dotenv from "dotenv"; + +dotenv.config(); + +const KEY = process.env.PRIVATE_KEY as string; export default async function (hre: HardhatRuntimeEnvironment) { // Private key of the account used to deploy - const wallet = new Wallet(""); + const wallet = new Wallet(KEY); const deployer = new Deployer(hre, wallet); const factoryArtifact = await deployer.loadArtifact("AAFactory"); const aaArtifact = await deployer.loadArtifact("TwoUserMultisig"); diff --git a/deploy/deploy-multisig.ts b/deploy/deploy-multisig.ts index 7acfa74..cb7d457 100644 --- a/deploy/deploy-multisig.ts +++ b/deploy/deploy-multisig.ts @@ -1,14 +1,19 @@ import { utils, Wallet, Provider, EIP712Signer, types } from "zksync-web3"; import * as ethers from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; +import dotenv from "dotenv"; + +dotenv.config(); + +const KEY = process.env.PRIVATE_KEY as string; // Put the address of your AA factory -const AA_FACTORY_ADDRESS = ""; +const AA_FACTORY_ADDRESS = "0xb3AE0580Fff458E48Eb97EcDaD7Bbe825b78F410"; export default async function (hre: HardhatRuntimeEnvironment) { const provider = new Provider("https://testnet.era.zksync.dev"); // Private key of the account used to deploy - const wallet = new Wallet("").connect(provider); + const wallet = new Wallet(KEY).connect(provider); const factoryArtifact = await hre.artifacts.readArtifact("AAFactory"); const aaFactory = new ethers.Contract( diff --git a/hardhat.config.ts b/hardhat.config.ts index aa0ccf5..c132ebd 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -2,6 +2,7 @@ import { HardhatUserConfig } from "hardhat/config"; import "@matterlabs/hardhat-zksync-deploy"; import "@matterlabs/hardhat-zksync-solc"; +import "@matterlabs/hardhat-zksync-node"; const config: HardhatUserConfig = { zksolc: { @@ -20,6 +21,12 @@ const config: HardhatUserConfig = { ethNetwork: "goerli", // Can also be the RPC URL of the network (e.g. `https://goerli.infura.io/v3/`) zksync: true, }, + zkSyncLocalnet: { + url: "http://127.0.0.1:8011", + ethNetwork: "http://127.0.0.1:8545", + zksync: true, + chainId: 260, + } }, solidity: { version: "0.8.17", diff --git a/package.json b/package.json index b7bb788..af963b3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,16 @@ "version": "1.0.0", "main": "index.js", "license": "MIT", + "scripts": { + "test": "hardhat test --network hardhat", + "deploy:factory": "hardhat deploy-zksync --script deploy-factory.ts --network zkSyncTestnet", + "deploy:multisig": "hardhat deploy-zksync --script deploy-multisig.ts --network zkSyncTestnet" + }, "devDependencies": { + "@types/chai": "^4.3.10", + "@types/mocha": "^10.0.4", + "chai": "^4.3.10", + "dotenv": "^16.3.1", "ethers": "^5.7.0", "ts-node": "^10.9.1", "typescript": "^4.8.4" @@ -11,6 +20,7 @@ "dependencies": { "@matterlabs/hardhat-zksync-deploy": "^0.6.3", "@matterlabs/hardhat-zksync-solc": "^0.4.0", + "@matterlabs/hardhat-zksync-node": "0.0.1-beta.6", "@matterlabs/zksync-contracts": "^0.6.1", "@openzeppelin/contracts": "^4.7.3", "hardhat": "^2.12.0", diff --git a/test/main.test.ts b/test/main.test.ts new file mode 100644 index 0000000..79124d6 --- /dev/null +++ b/test/main.test.ts @@ -0,0 +1,88 @@ +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import { expect } from "chai"; +import * as hre from "hardhat"; +import { BigNumber, ethers } from "ethers"; +import * as zks from "zksync-web3"; +import { deployFactory, deployMultisig, fundAccount, MultiSigWallet, signMultiSigTx } from "./utils"; + +const config = { + firstWalletPrivateKey: "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110", + firstWalletAddress: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", +}; + +describe("Account Abstraction Tests", function () { + let accountContractName: string; + let accountContract: zks.Contract; + let factoryContractName: string; + let factoryContract: zks.Contract; + let provider: zks.Provider; + let firstRichWallet: zks.Wallet; + let result: any; + + before(async function () { + provider = new zks.Provider(hre.network.config.url); + firstRichWallet = new zks.Wallet(config.firstWalletPrivateKey, provider); + }); + + describe("Old Example Account Abstraction Tests", function () { + accountContractName = "TwoUserMultisig"; + factoryContractName = "AAFactory"; + describe("OldAccountFactory", function () { + before(async function () { + factoryContract = await deployFactory(firstRichWallet, accountContractName, factoryContractName); + }); + + it("Should have a tx hash that starts from 0x", async function () { + result = factoryContract.deployTransaction.hash; + expect(result).to.contains("0x"); + }); + }); + + describe("OldAccount", async function () { + let ownerWallet1: zks.Wallet; + let ownerWallet2: zks.Wallet; + let multiSigWallet: MultiSigWallet; + before(async function () { + ownerWallet1 = zks.Wallet.createRandom(); + ownerWallet2 = zks.Wallet.createRandom(); + accountContract = await deployMultisig(firstRichWallet, factoryContract.address, ownerWallet1, ownerWallet2); + await fundAccount(firstRichWallet, accountContract.address); + await signMultiSigTx(firstRichWallet, accountContract.address, factoryContract.address, ownerWallet1, ownerWallet2); + }); + + it("Should have a tx hash that starts from 0x", async function () { + result = factoryContract.deployTransaction.hash; + expect(result).to.contains("0x"); + }); + + it("Should have a balance", async function () { + const result = await accountContract.provider.getBalance(accountContract.address); + // Convert BigNumber to a primitive number for comparison + const balance = parseFloat(ethers.utils.formatEther(result)); + expect(balance).to.be.greaterThan(99.99); + }); + + it("Should be able to send 10 ETH to the main wallet", async function () { + multiSigWallet = new MultiSigWallet( + accountContract.address, + ownerWallet1.privateKey, + ownerWallet2.privateKey, + provider + ); + const balanceBefore = (await provider.getBalance(firstRichWallet.address)).toBigInt(); + await ( + await multiSigWallet.transfer({ + to: firstRichWallet.address, + amount: ethers.utils.parseUnits("10", 18), + overrides: { type: 113 }, + }) + ).wait(); + const balance = (await provider.getBalance(firstRichWallet.address)).toBigInt(); + const difference = balanceBefore - balance; + // expect to be slightly higher than 5 + expect(difference / BigInt(10 ** 18) > 4.9).to.be.true; + expect(difference / BigInt(10 ** 18) < 5.1).to.be.true; + }); + }); + }); +}); diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 0000000..840d981 --- /dev/null +++ b/test/utils.ts @@ -0,0 +1,169 @@ +import { utils, Wallet, Provider, types, EIP712Signer } from "zksync-web3"; +import * as hre from "hardhat"; +import { ethers } from "ethers"; +import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; + +export async function deployFactory(wallet: Wallet, accountContractName: string, accountFactoryContractName: string) { + const deployer = new Deployer(hre, wallet); + const accountFactoryArtifact = await deployer.loadArtifact(accountFactoryContractName); + const accountArtifact = await deployer.loadArtifact(accountContractName); + + // Getting the bytecodeHash of the account + const bytecodeHash = utils.hashBytecode(accountArtifact.bytecode); + + let accountFactory = await deployer.deploy(accountFactoryArtifact, [bytecodeHash], undefined, [ + // Since the factory requires the code of the multisig to be available, + // we should pass it here as well. + accountArtifact.bytecode, + ]); + + return accountFactory; +} + +export async function deployMultisig(wallet: Wallet, factoryAddress: string, ownerWallet1: Wallet, ownerWallet2: Wallet) { + const factoryArtifact = await hre.artifacts.readArtifact("AAFactory"); + const accountArtifact = await hre.artifacts.readArtifact("TwoUserMultisig"); + + const aaFactory = new ethers.Contract(factoryAddress, factoryArtifact.abi, wallet); + + // For the simplicity of the tutorial, we will use zero hash as salt + const salt = ethers.constants.HashZero; + + // deploy account owned by owner1 & owner2 + const tx = await aaFactory.deployAccount(salt, ownerWallet1.address, ownerWallet2.address, { gasLimit: 10000000 }); + await tx.wait(); + + // Getting the address of the deployed contract account + const abiCoder = new ethers.utils.AbiCoder(); + let multisigAddress = utils.create2Address(factoryAddress, await aaFactory.aaBytecodeHash(), salt, abiCoder.encode(["address", "address"], [ownerWallet1.address, ownerWallet2.address])); + + const accountContract = new ethers.Contract(multisigAddress, accountArtifact.abi, wallet); + return accountContract; +} + +export async function signMultiSigTx(wallet: Wallet, multiSigAddress: string, factoryAddress: string, owner1: Wallet, owner2: Wallet) { + const factoryArtifact = await hre.artifacts.readArtifact("AAFactory"); + const aaFactory = new ethers.Contract(factoryAddress, factoryArtifact.abi, wallet); + const provider = wallet.provider; + const salt = ethers.constants.HashZero; + // Transaction to deploy a new account using the multisig we just deployed + let aaTx = await aaFactory.populateTransaction.deployAccount( + salt, + // These are accounts that will own the newly deployed account + Wallet.createRandom().address, + Wallet.createRandom().address + ); + + const gasLimit = await provider.estimateGas(aaTx); + const gasPrice = await provider.getGasPrice(); + + aaTx = { + ...aaTx, + // deploy a new account using the multisig + from: multiSigAddress, + gasLimit: gasLimit, + gasPrice: gasPrice, + chainId: (await provider.getNetwork()).chainId, + nonce: await provider.getTransactionCount(multiSigAddress), + type: 113, + customData: { + gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, + } as types.Eip712Meta, + value: ethers.BigNumber.from(0), + }; + const signedTxHash = EIP712Signer.getSignedDigest(aaTx); + + const signature = ethers.utils.concat([ + // Note, that `signMessage` wouldn't work here, since we don't want + // the signed hash to be prefixed with `\x19Ethereum Signed Message:\n` + ethers.utils.joinSignature(owner1._signingKey().signDigest(signedTxHash)), + ethers.utils.joinSignature(owner2._signingKey().signDigest(signedTxHash)), + ]); + + aaTx.customData = { + ...aaTx.customData, + customSignature: signature, + }; + + const sentTx = await provider.sendTransaction(utils.serialize(aaTx)); + await sentTx.wait(); + return sentTx; +} + +export async function deployAccount(wallet: Wallet, factoryAddress: string, accountOwnerPublicKey: string) { + const paFactoryArtifact = await hre.artifacts.readArtifact("PensionAccountFactory"); + const paFactory = new ethers.Contract(factoryAddress, paFactoryArtifact.abi, wallet); + + // Account owner address + const owner = ethers.utils.getAddress(accountOwnerPublicKey); + + // Contract constructor args + const dex = "0x123dex"; + const doge = "0x123doge"; + const pepe = "0x123pepe"; + const shib = "0x123shib"; + const btc = "0x123btc"; + + // For the simplicity of the tutorial, we will use zero hash as salt + const salt = ethers.constants.HashZero; + + // deploy account with dex and token addresses + const tx = await paFactory.deployAccount(salt, owner, dex, doge, pepe, shib, btc, { gasLimit: 10000000 }); + await tx.wait(); + + // Getting the address of the deployed contract account + const abiCoder = new ethers.utils.AbiCoder(); + let multisigAddress = utils.create2Address( + factoryAddress, + await paFactory.aaBytecodeHash(), + salt, + abiCoder.encode(["owner", "dex", "doge", "pepe", "shib", "btc"], [owner, dex, doge, pepe, shib, btc]) + ); + + const pensionAccountContract = new ethers.Contract(multisigAddress, paFactoryArtifact.abi, wallet); + return pensionAccountContract; +} + +export async function fundAccount(wallet: Wallet, destinationAddress: string, amount: string = "100") { + // Send funds to the account + await ( + await wallet.sendTransaction({ + to: destinationAddress, + // You can increase the amount of ETH sent to the multisig + value: ethers.utils.parseEther(amount), + }) + ).wait(); +} + +// Temporary wallet for testing - that is accepting two private keys - and signs the transaction with both. +export class MultiSigWallet extends Wallet { + readonly aaAddress: string; + otherWallet: Wallet; + + // AA_address - is the account abstraction address for which, we'll use the private key to sign transactions. + constructor( + aaAddress: string, + privateKey1: string, + privateKey2: string, + providerL2: Provider, + ) { + super(privateKey1, providerL2); + this.otherWallet = new Wallet(privateKey2, providerL2); + this.aaAddress = aaAddress; + } + + getAddress(): Promise { + return Promise.resolve(this.aaAddress); + } + + async signTransaction(transaction: types.TransactionRequest) { + const sig1 = await this.eip712.sign(transaction); + const sig2 = await this.otherWallet.eip712.sign(transaction); + // substring(2) to remove the '0x' from sig2. + if (transaction.customData === undefined) { + throw new Error("Transaction customData is undefined"); + } + transaction.customData.customSignature = sig1 + sig2.substring(2); + return (0, utils.serialize)(transaction); + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 40e21ad..9a4b92b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -417,15 +417,39 @@ dependencies: chalk "4.1.2" -"@matterlabs/hardhat-zksync-solc@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.4.0.tgz#4f0352398a9de9144fd2c0e32295e212318e164c" - integrity sha512-lCHozNmgLco7WOKSCG0FaywAT2Hdymlb2EZF3J4ly4gMVkz54LXXXFLtibggWH5OdYmYkMLGRvUdq8w/ilJUjQ== +"@matterlabs/hardhat-zksync-node@0.0.1-beta.6": + version "0.0.1-beta.6" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-node/-/hardhat-zksync-node-0.0.1-beta.6.tgz#7bfd4af1820335a10f78dee864c5b49da5a1a3bd" + integrity sha512-YwPkfOLm+p6YZJuta3yCgJnUM2LM+w+jOPkTyLZBS3a1G2ozWvPhOtShsSXU2M2HVHhA+ubkx/+rk6zYOZiXfQ== + dependencies: + "@matterlabs/hardhat-zksync-solc" "1.0.0" + axios "^1.4.0" + chalk "4.1.2" + fs-extra "^11.1.1" + ts-morph "^19.0.0" + +"@matterlabs/hardhat-zksync-solc@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.0.0.tgz#4da54b59fb4fa9c2ba22a3bb2e6150cf1d70028c" + integrity sha512-N6uUcmbAwZtu60WooUGJ1ssUdFFCd8MU1PdOSXcCJQ3vzjej2p3Ra8t3DrbJSWf3LRJoM18mZkZD2AktlF/ONw== + dependencies: + "@nomiclabs/hardhat-docker" "^2.0.0" + chalk "4.1.2" + dockerode "^3.3.4" + fs-extra "^11.1.1" + proper-lockfile "^4.1.2" + semver "^7.5.1" + +"@matterlabs/hardhat-zksync-solc@^0.4.0": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.4.2.tgz#64121082e88c5ab22eb4e9594d120e504f6af499" + integrity sha512-6NFWPSZiOAoo7wNuhMg4ztj7mMEH+tLrx09WuCbcURrHPijj/KxYNsJD6Uw5lapKr7G8H7SQISGid1/MTXVmXQ== dependencies: "@nomiclabs/hardhat-docker" "^2.0.0" chalk "4.1.2" dockerode "^3.3.4" fs-extra "^11.1.1" + proper-lockfile "^4.1.2" semver "^7.5.1" "@matterlabs/zksync-contracts@^0.6.1": @@ -454,6 +478,27 @@ resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@nomicfoundation/ethereumjs-block@5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz#6f89664f55febbd723195b6d0974773d29ee133d" @@ -758,6 +803,16 @@ "@sentry/types" "5.30.0" tslib "^1.9.3" +"@ts-morph/common@~0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.20.0.tgz#3f161996b085ba4519731e4d24c35f6cba5b80af" + integrity sha512-7uKjByfbPpwuzkstL3L5MQyuXPSKdoNG93Fmi2JoDcTf3pEP731JdRFAduRVkOs8oqxPsXKA+ScrWkdQ8t/I+Q== + dependencies: + fast-glob "^3.2.12" + minimatch "^7.4.3" + mkdirp "^2.1.6" + path-browserify "^1.0.1" + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -792,11 +847,21 @@ dependencies: "@types/node" "*" +"@types/chai@^4.3.10": + version "4.3.10" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.10.tgz#2ad2959d1767edee5b0e4efb1a0cd2b500747317" + integrity sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg== + "@types/lru-cache@^5.1.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== +"@types/mocha@^10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.4.tgz#b5331955ebca216604691fd4fcd2dbdc2bd559a4" + integrity sha512-xKU7bUjiFTIttpWaIZ9qvgg+22O1nmbA+HRxdlR+u6TWsGfmFdXrheJoK4fFxrHNVIOBDvDNKZG+LYBpMHpX3w== + "@types/node@*": version "18.16.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.1.tgz#5db121e9c5352925bb1f1b892c4ae620e3526799" @@ -948,6 +1013,25 @@ asn1@^0.2.6: dependencies: safer-buffer "~2.1.0" +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== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.4.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.1.tgz#76550d644bf0a2d469a01f9244db6753208397d7" + integrity sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1034,7 +1118,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1173,6 +1257,19 @@ catering@^2.1.0, catering@^2.1.1: resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== +chai@^4.3.10: + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== + dependencies: + 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.1.2, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -1190,6 +1287,13 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +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, chokidar@^3.4.0: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -1248,6 +1352,11 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +code-block-writer@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770" + integrity sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1272,6 +1381,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + command-exists@^1.2.8: version "1.2.9" resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" @@ -1367,6 +1483,18 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +deep-eql@^4.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: + type-detect "^4.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -1420,6 +1548,11 @@ dockerode@^3.3.4: docker-modem "^3.0.0" tar-fs "~2.0.1" +dotenv@^16.3.1: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -1581,6 +1714,24 @@ evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +fast-glob@^3.2.12: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1613,6 +1764,20 @@ follow-redirects@^1.12.1: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.15.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fp-ts@1.19.3: version "1.19.3" resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" @@ -1682,6 +1847,11 @@ get-caller-file@^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.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-intrinsic@^1.0.2: version "1.2.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" @@ -1691,7 +1861,7 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.3" -glob-parent@~5.1.2: +glob-parent@^5.1.2, 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== @@ -1722,7 +1892,7 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4: 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== @@ -2066,6 +2236,13 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +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: + get-func-name "^2.0.1" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -2118,6 +2295,31 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + 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" @@ -2142,6 +2344,13 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" +minimatch@^7.4.3: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -2159,6 +2368,11 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" +mkdirp@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" + integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== + mnemonist@^0.38.0: version "0.38.5" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" @@ -2307,6 +2521,11 @@ p-try@^1.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -2327,6 +2546,11 @@ path-parse@^1.0.6: 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== + pbkdf2@^3.0.17: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -2338,7 +2562,7 @@ pbkdf2@^3.0.17: safe-buffer "^5.0.1" sha.js "^2.4.8" -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -2348,6 +2572,20 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +proper-lockfile@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pump@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" @@ -2449,6 +2687,16 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^2.2.8: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -2478,6 +2726,13 @@ run-parallel-limit@^1.1.0: dependencies: queue-microtask "^1.2.2" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + rustbn.js@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" @@ -2563,6 +2818,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + solc@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" @@ -2770,6 +3030,14 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +ts-morph@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-19.0.0.tgz#43e95fb0156c3fe3c77c814ac26b7d0be2f93169" + integrity sha512-D6qcpiJdn46tUqV45vr5UGM2dnIEuTGNxVhg0sk5NX11orcouwj6i1bMqZIz2mZTZB1Hcgy7C3oEVhAT+f6mbQ== + dependencies: + "@ts-morph/common" "~0.20.0" + code-block-writer "^12.0.0" + ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -2814,6 +3082,11 @@ tweetnacl@^1.0.3: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== +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-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"