From d7300bc7078446486efef5047adf11d950e03b37 Mon Sep 17 00:00:00 2001 From: 0xRaccoon Date: Thu, 21 Dec 2023 09:31:41 -0300 Subject: [PATCH] test: add Governor unit tests Signed-off-by: 0xRaccoon --- package.json | 3 +- remappings.txt | 1 + solidity/examples/AliceGovernor.sol | 12 +- solidity/test/smock/SmockHelper.sol | 20 ++ .../test/smock/examples/MockAliceGovernor.sol | 271 ++++++++++++++++++ .../test/smock/examples/MockRabbitToken.sol | 147 ++++++++++ solidity/test/unit/WonderGovernor.t.sol | 94 ++++++ yarn.lock | 46 ++- 8 files changed, 584 insertions(+), 10 deletions(-) create mode 100644 solidity/test/smock/SmockHelper.sol create mode 100644 solidity/test/smock/examples/MockAliceGovernor.sol create mode 100644 solidity/test/smock/examples/MockRabbitToken.sol create mode 100644 solidity/test/unit/WonderGovernor.t.sol diff --git a/package.json b/package.json index ffa408b..01ed179 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "package.json": "sort-package-json" }, "dependencies": { - "@openzeppelin/contracts": "^5.0.1", + "@defi-wonderland/smock-foundry": "1.0.6", + "@openzeppelin/contracts": "5.0.1", "isolmate": "github:defi-wonderland/isolmate#59e1804" }, "devDependencies": { diff --git a/remappings.txt b/remappings.txt index 83b675a..db861fd 100644 --- a/remappings.txt +++ b/remappings.txt @@ -4,4 +4,5 @@ isolmate/=node_modules/isolmate/src contracts/=solidity/contracts interfaces/=solidity/interfaces +examples/=solidity/examples test/=solidity/test \ No newline at end of file diff --git a/solidity/examples/AliceGovernor.sol b/solidity/examples/AliceGovernor.sol index 75b2930..74eee89 100644 --- a/solidity/examples/AliceGovernor.sol +++ b/solidity/examples/AliceGovernor.sol @@ -9,11 +9,11 @@ contract AliceGovernor is WonderGovernor { string internal _countingMode = 'support=bravo&quorum=bravo'; uint8[] internal __proposalTypes = [1, 2, 3]; - mapping(uint256 proposalId => mapping(address => Receipt)) public receipts; + mapping(uint256 proposalId => mapping(address => BallotReceipt)) public receipts; mapping(uint256 proposalId => ProposalTrack) public proposalTracks; /// @notice Ballot receipt record for a voter - struct Receipt { + struct BallotReceipt { /// @notice Whether or not a vote has been cast bool hasVoted; /// @notice 0 = Against, 1 = For, 2 = Abstain @@ -95,11 +95,11 @@ contract AliceGovernor is WonderGovernor { revert InvalidVoteType(_support); } - Receipt storage receipt = receipts[_proposalId][_account]; + BallotReceipt storage _receipt = receipts[_proposalId][_account]; - receipt.hasVoted = true; - receipt.support = _support; - receipt.votes = _weight; + _receipt.hasVoted = true; + _receipt.support = _support; + _receipt.votes = _weight; } function hasVoted(uint256 _proposalId, address _account) external view override returns (bool) { diff --git a/solidity/test/smock/SmockHelper.sol b/solidity/test/smock/SmockHelper.sol new file mode 100644 index 0000000..442961e --- /dev/null +++ b/solidity/test/smock/SmockHelper.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Test} from 'forge-std/Test.sol'; + +contract SmockHelper is Test { + function deployMock( + string memory _label, + bytes memory _creationCode, + bytes memory _encodedArgs + ) internal returns (address _deployed) { + bytes memory _bytecode = abi.encodePacked(_creationCode, _encodedArgs); + assembly { + mstore(0x0, _creationCode) + _deployed := create2(0, add(_bytecode, 0x20), mload(_bytecode), 'Wonderland') + } + vm.label(_deployed, _label); + vm.allowCheatcodes(_deployed); + } +} diff --git a/solidity/test/smock/examples/MockAliceGovernor.sol b/solidity/test/smock/examples/MockAliceGovernor.sol new file mode 100644 index 0000000..5b2623d --- /dev/null +++ b/solidity/test/smock/examples/MockAliceGovernor.sol @@ -0,0 +1,271 @@ +/// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Test} from 'forge-std/Test.sol'; +import { + Address, + AliceGovernor, + Checkpoints, + Context, + DoubleEndedQueue, + ECDSA, + EIP712, + ERC165, + IERC1155Receiver, + IERC165, + IERC6372, + IERC721Receiver, + IWonderGovernor, + IWonderVotes, + Nonces, + SafeCast, + SignatureChecker, + Time, + WonderGovernor, + WonderVotes +} from 'solidity/examples/AliceGovernor.sol'; +import 'solidity/contracts/governance/WonderGovernor.sol'; +import 'solidity/contracts/governance/utils/WonderVotes.sol'; + +contract MockAliceGovernor is AliceGovernor, Test { + constructor(address _wonderToken) AliceGovernor(_wonderToken) {} + + /// Mocked State Variables + + function set_votes(WonderVotes _votes) public { + votes = _votes; + } + + function mock_call_votes(WonderVotes _votes) public { + vm.mockCall(address(this), abi.encodeWithSignature('votes()'), abi.encode(_votes)); + } + + function set__countingMode(string memory __countingMode) public { + _countingMode = __countingMode; + } + + function set___proposalTypes(uint8[] memory ___proposalTypes) public { + __proposalTypes = ___proposalTypes; + } + + function set_receipts(uint256 _key0, address _key1, AliceGovernor.BallotReceipt memory _value) public { + receipts[_key0][_key1] = _value; + } + + function mock_call_receipts(uint256 _key0, address _key1, AliceGovernor.BallotReceipt memory _value) public { + vm.mockCall(address(this), abi.encodeWithSignature('receipts(uint256,address)', _key0, _key1), abi.encode(_value)); + } + + function set_proposalTracks(uint256 _key0, AliceGovernor.ProposalTrack memory _value) public { + proposalTracks[_key0] = _value; + } + + function mock_call_proposalTracks(uint256 _key0, AliceGovernor.ProposalTrack memory _value) public { + vm.mockCall(address(this), abi.encodeWithSignature('proposalTracks(uint256)', _key0), abi.encode(_value)); + } + + /// Mocked External Functions + + function mock_call_CLOCK_MODE(string memory _clockMode) public { + vm.mockCall(address(this), abi.encodeWithSignature('CLOCK_MODE()'), abi.encode(_clockMode)); + } + + function mock_call_COUNTING_MODE(string memory _return0) public { + vm.mockCall(address(this), abi.encodeWithSignature('COUNTING_MODE()'), abi.encode(_return0)); + } + + function mock_call_clock(uint48 _return0) public { + vm.mockCall(address(this), abi.encodeWithSignature('clock()'), abi.encode(_return0)); + } + + function mock_call_votingPeriod(uint256 _return0) public { + vm.mockCall(address(this), abi.encodeWithSignature('votingPeriod()'), abi.encode(_return0)); + } + + function mock_call_votingDelay(uint256 _return0) public { + vm.mockCall(address(this), abi.encodeWithSignature('votingDelay()'), abi.encode(_return0)); + } + + function mock_call_quorum(uint256 _timepoint, uint8 _proposalType, uint256 _return0) public { + vm.mockCall( + address(this), abi.encodeWithSignature('quorum(uint256,uint8)', _timepoint, _proposalType), abi.encode(_return0) + ); + } + + function mock_call_hasVoted(uint256 _proposalId, address _account, bool _return0) public { + vm.mockCall( + address(this), abi.encodeWithSignature('hasVoted(uint256,address)', _proposalId, _account), abi.encode(_return0) + ); + } + + function mock_call_proposalThreshold(uint8 _proposalType, uint256 _return0) public { + vm.mockCall(address(this), abi.encodeWithSignature('proposalThreshold(uint8)', _proposalType), abi.encode(_return0)); + } + + function mock_call_isValidProposalType(uint8 _proposalType, bool _return0) public { + vm.mockCall( + address(this), abi.encodeWithSignature('isValidProposalType(uint8)', _proposalType), abi.encode(_return0) + ); + } + + /// Mocked Internal Functions + + struct _getVotesOutput { + uint256 _returnParam0; + } + + mapping(bytes32 => _getVotesOutput) private _getVotesOutputs; + bytes32[] private _getVotesInputHashes; + + function mock_call__getVotes( + address _account, + uint8 _proposalType, + uint256 _timepoint, + bytes memory _params, + uint256 _returnParam0 + ) public { + bytes32 _key = keccak256(abi.encode(_account, _proposalType, _timepoint, _params)); + _getVotesOutputs[_key] = _getVotesOutput(_returnParam0); + for (uint256 _i; _i < _getVotesInputHashes.length; ++_i) { + if (_key == _getVotesInputHashes[_i]) { + return; + } + } + _getVotesInputHashes.push(_key); + } + + function _getVotes( + address _account, + uint8 _proposalType, + uint256 _timepoint, + bytes memory _params + ) internal view override returns (uint256 _returnParam0) { + bytes32 _key = keccak256(abi.encode(_account, _proposalType, _timepoint, _params)); + for (uint256 _i; _i < _getVotesInputHashes.length; ++_i) { + if (_key == _getVotesInputHashes[_i]) { + _getVotesOutput memory _output = _getVotesOutputs[_key]; + return (_output._returnParam0); + } + } + return super._getVotes(_account, _proposalType, _timepoint, _params); + } + + struct _isValidProposalTypeOutput { + bool _returnParam0; + } + + mapping(bytes32 => _isValidProposalTypeOutput) private _isValidProposalTypeOutputs; + bytes32[] private _isValidProposalTypeInputHashes; + + function mock_call__isValidProposalType(uint8 _proposalType, bool _returnParam0) public { + bytes32 _key = keccak256(abi.encode(_proposalType)); + _isValidProposalTypeOutputs[_key] = _isValidProposalTypeOutput(_returnParam0); + for (uint256 _i; _i < _isValidProposalTypeInputHashes.length; ++_i) { + if (_key == _isValidProposalTypeInputHashes[_i]) { + return; + } + } + _isValidProposalTypeInputHashes.push(_key); + } + + function _isValidProposalType(uint8 _proposalType) internal view override returns (bool _returnParam0) { + bytes32 _key = keccak256(abi.encode(_proposalType)); + for (uint256 _i; _i < _isValidProposalTypeInputHashes.length; ++_i) { + if (_key == _isValidProposalTypeInputHashes[_i]) { + _isValidProposalTypeOutput memory _output = _isValidProposalTypeOutputs[_key]; + return (_output._returnParam0); + } + } + return super._isValidProposalType(_proposalType); + } + + function mock_call__countVote( + uint256 _proposalId, + address _account, + uint8 _support, + uint256 _weight, + bytes memory _params + ) public { + vm.mockCall( + address(this), + abi.encodeWithSignature( + '_countVote(uint256,address,uint8,uint256,bytes)', _proposalId, _account, _support, _weight, _params + ), + abi.encode() + ); + } + + function _countVote( + uint256 _proposalId, + address _account, + uint8 _support, + uint256 _weight, + bytes memory _params + ) internal override { + (bool _success, bytes memory _data) = address(this).call( + abi.encodeWithSignature( + '_countVote(uint256,address,uint8,uint256,bytes)', _proposalId, _account, _support, _weight, _params + ) + ); + if (_success) return abi.decode(_data, ()); + else return super._countVote(_proposalId, _account, _support, _weight, _params); + } + + struct _quorumReachedOutput { + bool _returnParam0; + } + + mapping(bytes32 => _quorumReachedOutput) private _quorumReachedOutputs; + bytes32[] private _quorumReachedInputHashes; + + function mock_call__quorumReached(uint256 _proposalId, bool _returnParam0) public { + bytes32 _key = keccak256(abi.encode(_proposalId)); + _quorumReachedOutputs[_key] = _quorumReachedOutput(_returnParam0); + for (uint256 _i; _i < _quorumReachedInputHashes.length; ++_i) { + if (_key == _quorumReachedInputHashes[_i]) { + return; + } + } + _quorumReachedInputHashes.push(_key); + } + + function _quorumReached(uint256 _proposalId) internal view override returns (bool _returnParam0) { + bytes32 _key = keccak256(abi.encode(_proposalId)); + for (uint256 _i; _i < _quorumReachedInputHashes.length; ++_i) { + if (_key == _quorumReachedInputHashes[_i]) { + _quorumReachedOutput memory _output = _quorumReachedOutputs[_key]; + return (_output._returnParam0); + } + } + return super._quorumReached(_proposalId); + } + + struct _voteSucceededOutput { + bool _returnParam0; + } + + mapping(bytes32 => _voteSucceededOutput) private _voteSucceededOutputs; + bytes32[] private _voteSucceededInputHashes; + + function mock_call__voteSucceeded(uint256 _proposalId, bool _returnParam0) public { + bytes32 _key = keccak256(abi.encode(_proposalId)); + _voteSucceededOutputs[_key] = _voteSucceededOutput(_returnParam0); + for (uint256 _i; _i < _voteSucceededInputHashes.length; ++_i) { + if (_key == _voteSucceededInputHashes[_i]) { + return; + } + } + _voteSucceededInputHashes.push(_key); + } + + function _voteSucceeded(uint256 _proposalId) internal view override returns (bool _returnParam0) { + bytes32 _key = keccak256(abi.encode(_proposalId)); + for (uint256 _i; _i < _voteSucceededInputHashes.length; ++_i) { + if (_key == _voteSucceededInputHashes[_i]) { + _voteSucceededOutput memory _output = _voteSucceededOutputs[_key]; + return (_output._returnParam0); + } + } + return super._voteSucceeded(_proposalId); + } +} diff --git a/solidity/test/smock/examples/MockRabbitToken.sol b/solidity/test/smock/examples/MockRabbitToken.sol new file mode 100644 index 0000000..c1fa222 --- /dev/null +++ b/solidity/test/smock/examples/MockRabbitToken.sol @@ -0,0 +1,147 @@ +/// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Test} from 'forge-std/Test.sol'; +import {AliceGovernor, EIP712, ERC20, RabbitToken, WonderERC20Votes} from 'solidity/examples/RabbitToken.sol'; +import {WonderERC20Votes} from 'solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol'; +import {AliceGovernor} from 'solidity/examples/AliceGovernor.sol'; +import {EIP712} from 'node_modules/@openzeppelin/contracts/utils/cryptography/EIP712.sol'; +import {ERC20} from 'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'; + +contract MockRabbitToken is RabbitToken, Test { + constructor(AliceGovernor _governor) RabbitToken(_governor) {} + + /// Mocked State Variables + + function set_governor(AliceGovernor _governor) public { + governor = _governor; + } + + function mock_call_governor(AliceGovernor _governor) public { + vm.mockCall(address(this), abi.encodeWithSignature('governor()'), abi.encode(_governor)); + } + + /// Mocked External Functions + + function mock_call_proposalTypes(uint8[] memory _return0) public { + vm.mockCall(address(this), abi.encodeWithSignature('proposalTypes()'), abi.encode(_return0)); + } + + /// Mocked Internal Functions + + struct _getProposalTypesOutput { + uint8[] _returnParam0; + } + + mapping(bytes32 => _getProposalTypesOutput) private _getProposalTypesOutputs; + bytes32[] private _getProposalTypesInputHashes; + + function mock_call__getProposalTypes(uint8[] memory _returnParam0) public { + bytes32 _key = keccak256(abi.encode()); + _getProposalTypesOutputs[_key] = _getProposalTypesOutput(_returnParam0); + for (uint256 _i; _i < _getProposalTypesInputHashes.length; ++_i) { + if (_key == _getProposalTypesInputHashes[_i]) { + return; + } + } + _getProposalTypesInputHashes.push(_key); + } + + function _getProposalTypes() internal view override returns (uint8[] memory _returnParam0) { + bytes32 _key = keccak256(abi.encode()); + for (uint256 _i; _i < _getProposalTypesInputHashes.length; ++_i) { + if (_key == _getProposalTypesInputHashes[_i]) { + _getProposalTypesOutput memory _output = _getProposalTypesOutputs[_key]; + return (_output._returnParam0); + } + } + return super._getProposalTypes(); + } + + struct _maxDelegatesOutput { + uint8 _returnParam0; + } + + mapping(bytes32 => _maxDelegatesOutput) private _maxDelegatesOutputs; + bytes32[] private _maxDelegatesInputHashes; + + function mock_call__maxDelegates(uint8 _returnParam0) public { + bytes32 _key = keccak256(abi.encode()); + _maxDelegatesOutputs[_key] = _maxDelegatesOutput(_returnParam0); + for (uint256 _i; _i < _maxDelegatesInputHashes.length; ++_i) { + if (_key == _maxDelegatesInputHashes[_i]) { + return; + } + } + _maxDelegatesInputHashes.push(_key); + } + + function _maxDelegates() internal view override returns (uint8 _returnParam0) { + bytes32 _key = keccak256(abi.encode()); + for (uint256 _i; _i < _maxDelegatesInputHashes.length; ++_i) { + if (_key == _maxDelegatesInputHashes[_i]) { + _maxDelegatesOutput memory _output = _maxDelegatesOutputs[_key]; + return (_output._returnParam0); + } + } + return super._maxDelegates(); + } + + struct _validProposalTypeOutput { + bool _returnParam0; + } + + mapping(bytes32 => _validProposalTypeOutput) private _validProposalTypeOutputs; + bytes32[] private _validProposalTypeInputHashes; + + function mock_call__validProposalType(uint8 _proposalType, bool _returnParam0) public { + bytes32 _key = keccak256(abi.encode(_proposalType)); + _validProposalTypeOutputs[_key] = _validProposalTypeOutput(_returnParam0); + for (uint256 _i; _i < _validProposalTypeInputHashes.length; ++_i) { + if (_key == _validProposalTypeInputHashes[_i]) { + return; + } + } + _validProposalTypeInputHashes.push(_key); + } + + function _validProposalType(uint8 _proposalType) internal view override returns (bool _returnParam0) { + bytes32 _key = keccak256(abi.encode(_proposalType)); + for (uint256 _i; _i < _validProposalTypeInputHashes.length; ++_i) { + if (_key == _validProposalTypeInputHashes[_i]) { + _validProposalTypeOutput memory _output = _validProposalTypeOutputs[_key]; + return (_output._returnParam0); + } + } + return super._validProposalType(_proposalType); + } + + struct _weightNormalizerOutput { + uint256 _returnParam0; + } + + mapping(bytes32 => _weightNormalizerOutput) private _weightNormalizerOutputs; + bytes32[] private _weightNormalizerInputHashes; + + function mock_call__weightNormalizer(uint256 _returnParam0) public { + bytes32 _key = keccak256(abi.encode()); + _weightNormalizerOutputs[_key] = _weightNormalizerOutput(_returnParam0); + for (uint256 _i; _i < _weightNormalizerInputHashes.length; ++_i) { + if (_key == _weightNormalizerInputHashes[_i]) { + return; + } + } + _weightNormalizerInputHashes.push(_key); + } + + function _weightNormalizer() internal view override returns (uint256 _returnParam0) { + bytes32 _key = keccak256(abi.encode()); + for (uint256 _i; _i < _weightNormalizerInputHashes.length; ++_i) { + if (_key == _weightNormalizerInputHashes[_i]) { + _weightNormalizerOutput memory _output = _weightNormalizerOutputs[_key]; + return (_output._returnParam0); + } + } + return super._weightNormalizer(); + } +} diff --git a/solidity/test/unit/WonderGovernor.t.sol b/solidity/test/unit/WonderGovernor.t.sol new file mode 100644 index 0000000..df6909c --- /dev/null +++ b/solidity/test/unit/WonderGovernor.t.sol @@ -0,0 +1,94 @@ +import 'forge-std/Test.sol'; + +import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol'; +import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol'; +import {AliceGovernor} from 'examples/AliceGovernor.sol'; +import {MockRabbitToken} from '../smock/examples/MockRabbitToken.sol'; +import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol'; + +contract BaseTest is Test { + address deployer = makeAddr('deployer'); + address hatter = makeAddr('hatter'); + + IWonderGovernor governor; + MockRabbitToken rabbit; + + function _mockGetPastVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal { + vm.mockCall( + address(rabbit), + abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, _account, _proposalType, _timePoint), + abi.encode(_votes) + ); + } + + function setUp() public { + vm.startPrank(deployer); + + address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1); + governor = new AliceGovernor(tokenAddress); + rabbit = new MockRabbitToken(AliceGovernor(payable(address(governor)))); + + vm.stopPrank(); + } + + function _expectEmit(address _contract) internal { + vm.expectEmit(true, true, true, true, _contract); + } +} + +contract Unit_Propose is BaseTest { + event ProposalCreated( + uint256 proposalId, + uint8 proposalType, + address proposer, + address[] targets, + uint256[] values, + string[] signatures, + bytes[] calldatas, + uint256 voteStart, + uint256 voteEnd, + string description + ); + + function test_propose_Emit_ProposalCreated( + uint8 _proposalType, + address _target, + uint256 _value, + bytes memory _calldata, + string memory _description + ) public { + vm.assume(_proposalType < governor.proposalTypes().length); + + // hatter will pass the proposal threshold limit + _mockGetPastVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType)); + + address[] memory _targets = new address[](1); + _targets[0] = _target; + + uint256[] memory _values = new uint256[](1); + _values[0] = _value; + + bytes[] memory _calldatas = new bytes[](1); + _calldatas[0] = _calldata; + + uint256 _precomputedProposalId = + governor.hashProposal(_proposalType, _targets, _values, _calldatas, keccak256(bytes(_description))); + _expectEmit(address(governor)); + + emit ProposalCreated( + _precomputedProposalId, + _proposalType, + address(hatter), + _targets, + _values, + new string[](1), + _calldatas, + block.number + 1, + block.number + governor.votingPeriod() + 1, + _description + ); + + vm.prank(hatter); + uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, _description); + } +} diff --git a/yarn.lock b/yarn.lock index a80fc74..6d6371a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -192,6 +192,14 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@defi-wonderland/smock-foundry@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@defi-wonderland/smock-foundry/-/smock-foundry-1.0.6.tgz#25cef951d3ab4d038581a64d9d7cb55538bee53e" + integrity sha512-TEvPrkTBao1bMUnmIasBPdDQ46AC/pUFVrcvtvJ9Mc3krZkeZWGP+UDW786B9lGoGWkXFTWoiR6fZuoekczM1w== + dependencies: + handlebars "4.7.7" + yargs "17.7.2" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" @@ -231,7 +239,7 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@openzeppelin/contracts@^5.0.1": +"@openzeppelin/contracts@5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.1.tgz#93da90fc209a0a4ff09c1deb037fbb35e4020890" integrity sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w== @@ -1192,6 +1200,18 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: 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== +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -1701,7 +1721,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.6: +minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -1728,6 +1748,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -2262,6 +2287,11 @@ sort-package-json@1.53.1: is-plain-obj "2.1.0" sort-object-keys "^1.1.3" +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -2534,6 +2564,11 @@ type-fest@^1.0.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -2588,6 +2623,11 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2643,7 +2683,7 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^17.0.0: +yargs@17.7.2, yargs@^17.0.0: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==