diff --git a/.gitignore b/.gitignore index 0c0169e10ed5..9c0c41e2ce21 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ server_logs.txt core/lib/storage/.env /core/lib/multivm/benchmarks/*.csv +/core/tests/ts-integration/benchmarks/*.csv .zcli-config.json diff --git a/contracts b/contracts index 19b81f7c06d0..b4788f6cf09c 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 19b81f7c06d089cb307bfbfba33dd794e3a4cb90 +Subproject commit b4788f6cf09c47f8eb2c18e35399b3a611b81131 diff --git a/core/tests/ts-integration/evm-contracts/CounterFallback.sol b/core/tests/ts-integration/evm-contracts/CounterFallback.sol new file mode 100644 index 000000000000..2535a05262bf --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/CounterFallback.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract CounterFallback { + + function performCall() external { + uint256 value = 0; + value += 1; + } + + fallback() external { + this.performCall(); + } +} diff --git a/core/tests/ts-integration/evm-contracts/CreatorFallback.sol b/core/tests/ts-integration/evm-contracts/CreatorFallback.sol new file mode 100644 index 000000000000..ce532aa69b7e --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/CreatorFallback.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract Creation { + function blockNumber() external view returns (uint256) { + return block.number; + } +} + +contract CreatorFallback { + function performCall() external { + new Creation(); + type(Creation).runtimeCode; + } + fallback() external { + this.performCall(); + } +} diff --git a/core/tests/ts-integration/evm-contracts/GasCaller.sol b/core/tests/ts-integration/evm-contracts/GasCaller.sol new file mode 100644 index 000000000000..4fd43a235b5e --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/GasCaller.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract GasCaller { + uint256 _resultGas; + + function callAndGetGas(address _to) external returns (uint256){ + uint256 startGas = gasleft(); + // Just doing a call to an address + (bool success, ) = _to.call(""); + require(success); + _resultGas = startGas - gasleft(); + return _resultGas; + } +} diff --git a/core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol b/core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol new file mode 100644 index 000000000000..68d64fc074d8 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract OpcodeTestFallback { + + function performCall() external { + uint256 loaded = 1; + uint256 tmp; + uint256 prevBlock = block.number - 1; + assembly { + loaded := add(loaded, 1) + loaded := mul(loaded, 2) + loaded := sub(loaded, 1) + loaded := div(loaded, 2) + loaded := sdiv(loaded, 2) + loaded := mod(loaded, 2) + // ADDMOD + // MULMOD + loaded := exp(loaded, 2) + loaded := signextend(loaded, 2) + tmp := lt(loaded, 2) + tmp := gt(loaded, 2) + tmp := slt(loaded, 2) + tmp := sgt(loaded, 2) + tmp := eq(loaded, 2) + tmp := iszero(tmp) + tmp := and(1,1) + tmp := or(1,1) + tmp := xor(1,1) + tmp := not(tmp) + tmp := byte(tmp,1) + tmp := shl(tmp,1) + tmp := shr(tmp,1) + tmp := sar(tmp,1) + tmp := keccak256(0, 0x40) + tmp := address() + tmp := balance(0x00) + tmp := origin() + tmp := caller() + tmp := callvalue() + // CALLDATALOAD + tmp := calldatasize() + // CALLDATACOPY + tmp := codesize() + // CODECOPY + tmp := gasprice() + // EXTCODESIZE + // EXTCODECOPY + tmp := returndatasize() + // RETURNDATACOPY + // EXTCODEHASH + tmp := blockhash(prevBlock) + tmp := coinbase() + tmp := timestamp() + tmp := number() + tmp := prevrandao() + tmp := gaslimit() + tmp := chainid() + tmp := selfbalance() + tmp := basefee() + // POP + tmp := mload(1) + mstore(1024,1) + mstore8(10242,1) + tmp := sload(0) + sstore(0,1) + // JUMP + // JUMPI + // PC + tmp := msize() + tmp := gas() + // JUMPDEST + // PUSH0...PUSH32 + // DUP1...DUP16 + // SWAP1...SWAP16 + // LOG0...LOG4 + // CREATE + // CALL + // CALLCODE + // RETURN + // DELEGATECALL + // CREATE2 + // STATICCALL + // REVERT + // INVALID + // selfdestruct(sender) + tmp := calldataload(0) + calldatacopy(10,0,1) + codecopy(10,0,1) + tmp := extcodesize(0) + extcodecopy(address(),10,0,1) + returndatacopy(10,0,1) + pop(extcodehash(0)) + log0(0,30) + log1(0,30,30) + log2(0,30,30,30) + log3(0,30,30,30,30) + log4(0,30,30,30,30,30) + } + + // tmp = 0; + // tmp = 0x11; + // tmp = 0x2211; + // tmp = 0x332211; + // tmp = 0x44332211; + // tmp = 0x5544332211; + // tmp = 0x665544332211; + // tmp = 0x77665544332211; + // tmp = 0x8877665544332211; + // tmp = 0x998877665544332211; + // tmp = 0xaa998877665544332211; + // tmp = 0xbbaa998877665544332211; + // tmp = 0xccbbaa998877665544332211; + // tmp = 0xddccbbaa998877665544332211; + // tmp = 0xeeddccbbaa998877665544332211; + // tmp = 0xffeeddccbbaa998877665544332211; + // tmp = 0x11ffeeddccbbaa998877665544332211; + // tmp = 0x2211ffeeddccbbaa998877665544332211; + // tmp = 0x332211ffeeddccbbaa998877665544332211; + // tmp = 0x44332211ffeeddccbbaa998877665544332211; + // tmp = uint256(uint160(0x5544332211FFeeDDCcbbAa998877665544332211)); + // tmp = 0x665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x77665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x8877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0xff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x11ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x2211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x44332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x5544332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x665544332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x77665544332211ff998877665544332211ffeeddccbbaa998877665544332211; + } + + fallback() external { + this.performCall(); + } + +} diff --git a/core/tests/ts-integration/evm-contracts/UniswapFallback.sol b/core/tests/ts-integration/evm-contracts/UniswapFallback.sol new file mode 100644 index 000000000000..b2d631d3551d --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/UniswapFallback.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +interface IUniswapV2ERC20 { + event Approval(address indexed owner, address indexed spender, uint value); + event Transfer(address indexed from, address indexed to, uint value); + + function name() external pure returns (string memory); + + function symbol() external pure returns (string memory); + + function decimals() external pure returns (uint8); + + function totalSupply() external returns (uint); + + function balanceOf(address owner) external returns (uint); + + function allowance(address owner, address spender) external returns (uint); + + function approve(address spender, uint value) external returns (bool); + + function transfer(address to, uint value) external returns (bool); + + function transferFrom( + address from, + address to, + uint value + ) external returns (bool); + + function DOMAIN_SEPARATOR() external returns (bytes32); + + function PERMIT_TYPEHASH() external pure returns (bytes32); + + function nonces(address owner) external returns (uint); + + function permit( + address owner, + address spender, + uint value, + uint deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} + +interface IUniswapV2Pair { + event Mint(address indexed sender, uint amount0, uint amount1); + event Burn( + address indexed sender, + uint amount0, + uint amount1, + address indexed to + ); + event Swap( + address indexed sender, + uint amount0In, + uint amount1In, + uint amount0Out, + uint amount1Out, + address indexed to + ); + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint); + + function factory() external returns (address); + + function token0() external returns (address); + + function token1() external returns (address); + + function getReserves() + external + returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + + function price0CumulativeLast() external returns (uint); + + function price1CumulativeLast() external returns (uint); + + function kLast() external returns (uint); + + function mint(address to) external returns (uint liquidity); + + function burn(address to) external returns (uint amount0, uint amount1); + + function swap( + uint amount0Out, + uint amount1Out, + address to, + bytes calldata data + ) external; + + function skim(address to) external; + + function sync() external; + + function initialize(address, address) external; +} + +contract UniswapFallback { + IUniswapV2Pair public uniswapPair; + IUniswapV2ERC20 public uniswapPair2; + address public alice_address; + + function setUniswapAddress(address _uniswap_address) public { + uniswapPair = IUniswapV2Pair(_uniswap_address); + uniswapPair2 = IUniswapV2ERC20(_uniswap_address); + } + function setAliceAddress(address _alice_address) public { + alice_address = _alice_address; + } + // Fallback function + fallback() external { + // Implement any logic you want the contract to perform when it receives Ether + // This function will be called when the contract receives Ether and no other function matches the call data + uniswapPair.mint(alice_address); + uniswapPair.swap(0,5000,alice_address,"0x"); + uint balance = uniswapPair2.balanceOf(alice_address); + //uniswapPair2.transfer(address(uniswapPair),balance); + //uniswapPair.burn(alice_address); + } +} diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index 1eb9068c11eb..f5f693824757 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -31,6 +31,8 @@ "ts-jest": "^29.0.1", "ts-node": "^10.1.0", "typescript": "^4.3.5", - "zksync-web3": "^0.15.5" + "zksync-web3": "^0.15.5", + "csv-parser": "^3.0.0", + "csv-writer": "^1.6.0" } } diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index dd6830d58771..88900d8e9d18 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -93,6 +93,7 @@ export function getEVMArtifact(contractPath: string, contractName: string | unde const artifact = JSON.parse(solc.compile(JSON.stringify(compilerParams))).contracts['contract'][ contractName.split('.')[0] ]; + return artifact; } diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 00fa9cc873cb..f24b6074e45c 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -12,6 +12,10 @@ import { deployContract, getEVMArtifact, getEVMContractFactory, getTestContract import * as ethers from 'ethers'; import * as zksync from 'zksync-web3'; +import fs, { PathLike } from 'fs'; +import csv from 'csv-parser'; +import { createObjectCsvWriter } from 'csv-writer'; + const contracts = { tester: getTestContract('TestEVMCreate'), erc20: getTestContract('ERC20'), @@ -28,7 +32,12 @@ const artifacts = { uniswapV2Pair: getEVMArtifact('../contracts/uniswap-v2/UniswapV2Factory.sol', 'UniswapV2Pair.sol'), uniswapV2Factory: getEVMArtifact('../contracts/uniswap-v2/UniswapV2Factory.sol', 'UniswapV2Factory.sol'), opcodeTest: getEVMArtifact('../evm-contracts/OpcodeTest.sol'), - selfDestruct: getEVMArtifact('../evm-contracts/SelfDestruct.sol') + selfDestruct: getEVMArtifact('../evm-contracts/SelfDestruct.sol'), + gasCaller: getEVMArtifact('../evm-contracts/GasCaller.sol'), + counterFallback: getEVMArtifact('../evm-contracts/CounterFallback.sol'), + uniswapFallback: getEVMArtifact('../evm-contracts/UniswapFallback.sol'), + creatorFallback: getEVMArtifact('../evm-contracts/CreatorFallback.sol'), + opcodeTestFallback: getEVMArtifact('../evm-contracts/OpcodeTestFallback.sol') }; const initBytecode = '0x69602a60005260206000f3600052600a6016f3'; @@ -59,6 +68,41 @@ describe('EVM equivalence contract', () => { ).connect(alice); }); + describe('Gas consumption', () => { + test("Should compare gas against counter fallback contract's call", async () => { + const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); + + const counterContract = await deploygasCallerContract(alice, artifacts.counterFallback); + + let result = (await gasCallerContract.callStatic.callAndGetGas(counterContract.address)).toString(); + + const expected_gas = '3617'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + + test("Should compare gas against creator fallback contract's call", async () => { + const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); + + const creatorContract = await deploygasCallerContract(alice, artifacts.creatorFallback); + + let result = (await gasCallerContract.callStatic.callAndGetGas(creatorContract.address)).toString(); + + const expected_gas = '70601'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + + xtest("Should compare gas against opcode test fallback contract's call", async () => { + const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); + + const counterContract = await deploygasCallerContract(alice, artifacts.opcodeTestFallback); + + let result = (await gasCallerContract.callStatic.callAndGetGas(counterContract.address)).toString(); + + const expected_gas = '34763'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + }); + describe('Contract creation', () => { describe('Create from EOA', () => { test('Should create evm contract from EOA and allow view and non-view calls', async () => { @@ -447,6 +491,25 @@ describe('EVM equivalence contract', () => { dumpOpcodeLogs(evmLiquidityTransfer.transactionHash, alice.provider); dumpOpcodeLogs(evmBurnReceipt.transactionHash, alice.provider); }); + + test("Should compare gas against uniswap fallback contract's call", async () => { + const gasCallerFactory = getEVMContractFactory(alice, artifacts.gasCaller); + const gasCallerContract = await gasCallerFactory.deploy(); + await gasCallerContract.deployTransaction.wait(); + await alice.provider.getTransactionReceipt(gasCallerContract.deployTransaction.hash); + + const uniswapContract = await deploygasCallerContract(alice, artifacts.uniswapFallback); + await (await uniswapContract.setUniswapAddress(evmUniswapPair.address)).wait(); + await (await uniswapContract.setAliceAddress(alice.address)).wait(); + + await (await evmToken1.transfer(evmUniswapPair.address, 10000)).wait(); + await (await evmToken1.transfer(uniswapContract.address, 10000)).wait(); + + let result = (await gasCallerContract.callStatic.callAndGetGas(uniswapContract.address)).toString(); + + const expected_gas = '165939'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); }); // NOTE: Gas cost comparisons should be done on a *fresh* chain that doesn't have e.g. bytecodes already published @@ -473,6 +536,100 @@ describe('EVM equivalence contract', () => { }); }); +type BenchmarkResult = { + name: string; + used_zkevm_ergs: string; + used_evm_gas: string; + used_circuits: string; +}; + +async function saveBenchmark(name: string, filename: string, result: string) { + try { + const resultWithName = { + name: name, + used_zkevm_ergs: result, + used_evm_gas: '0', + used_circuits: '0' + }; + + let results: BenchmarkResult[] = []; + + // Read existing CSV file + if (fs.existsSync(filename)) { + const existingResults: BenchmarkResult[] = await new Promise((resolve, reject) => { + const results: BenchmarkResult[] = []; + fs.createReadStream(filename) + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', () => resolve(results)) + .on('error', reject); + }); + results = existingResults.map((result) => ({ + name: result.name, + used_zkevm_ergs: result.used_zkevm_ergs, + used_evm_gas: result.used_evm_gas, + used_circuits: result.used_circuits + })); + } + + // Push the new result + results.push(resultWithName); + + // Write results back to CSV + const csvWriter = createObjectCsvWriter({ + path: filename, + header: [ + { id: 'name', title: 'name' }, + { id: 'used_zkevm_ergs', title: 'used_zkevm_ergs' }, + { id: 'used_evm_gas', title: 'used_evm_gas' }, + { id: 'used_circuits', title: 'used_circuits' } + ] + }); + await csvWriter.writeRecords(results); + + console.log('Benchmark saved successfully.'); + } catch (error) { + console.error('Error saving benchmark:', error); + } +} +function zeroPad(num: number, places: number): string { + return String(num).padStart(places, '0'); +} + +async function startBenchmark(): Promise { + try { + const now = new Date(); + const year = now.getUTCFullYear(); + const month = zeroPad(now.getUTCMonth() + 1, 2); // Months are zero-based, so add 1 + const day = zeroPad(now.getUTCDate(), 2); + const hour = zeroPad(now.getUTCHours(), 2); + const minute = zeroPad(now.getUTCMinutes(), 2); + const second = zeroPad(now.getUTCSeconds(), 2); + const formattedTime = `${year}-${month}-${day}-${hour}-${minute}-${second}`; + const directoryPath = 'benchmarks'; + + if (!fs.existsSync(directoryPath)) { + // If it doesn't exist, create it + fs.mkdirSync(directoryPath); + } + + const filename = `benchmarks/benchmark_integration_${formattedTime}.csv`; + return filename; + } catch (error) { + console.error('Error creating benchmark:', error); + return ''; + } +} + +async function deploygasCallerContract(alice: zksync.Wallet, contract: any, ...args: Array) { + const counterFactory = getEVMContractFactory(alice, contract); + const counterContract = await counterFactory.deploy(...args); + await counterContract.deployTransaction.wait(); + await alice.provider.getTransactionReceipt(counterContract.deployTransaction.hash); + + return counterContract; +} + async function assertStoredBytecodeHash( deployer: zksync.Contract, deployedAddress: string, diff --git a/yarn.lock b/yarn.lock index 69fbd41f4e03..ba734450da73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5001,6 +5001,18 @@ css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +csv-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-3.0.0.tgz#b88a6256d79e090a97a1b56451f9327b01d710e7" + integrity sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ== + dependencies: + minimist "^1.2.0" + +csv-writer@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/csv-writer/-/csv-writer-1.6.0.tgz#d0cea44b6b4d7d3baa2ecc6f3f7209233514bcf9" + integrity sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -10852,7 +10864,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10869,6 +10881,15 @@ string-width@^2.1.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10933,7 +10954,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm: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== @@ -10954,6 +10975,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11826,7 +11854,16 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm: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" + +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==