diff --git a/.gitignore b/.gitignore index d95519b..d16445d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,6 @@ out/ .idea .vscode -# well, looks strange to ignore package-lock, but we have only pretter and it's temproray -package-lock.json node_modules # ignore foundry deploy artifacts diff --git a/README.md b/README.md index 167c451..a4f908f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,79 @@ This repository contains: - an [example proposal](./src/contracts/AddEmissionAdminPayload.sol) payload which could be used to setup liquidity mining on a governance controlled aave v3 pool - a [test](./tests/EmissionTestOpOptimism.t.sol) simulating the configuration of certain assets to receive liquidity mining +- a [test](./tests/EmissionConfigurationTestMATICXPolygon.t.sol) simulating the setting up of new configuration of certain assets after the liquidity mining program has been created + +## Instructions to activate Liquidity Mining on Aave V3: + +Screenshot 2023-04-10 at 11 27 10 AM + +1. Make sure the rewards funds that are needed to be distributed for Liquidity Mining are present in the Rewards Vault. + + _Note: The Rewards Vault is your address which contains the reward asset._ + +2. Do an ERC-20 approve of the total rewards to be distributed to the Transfer Strategy contract, this is contract by Aave which helps to pull the Liquidity Mining rewards from the Rewards Vault address to distribute to the user. To know more about how Transfer Strategy contract works you can check [here](https://github.com/aave/aave-v3-periphery/blob/master/docs/rewards/rewards-transfer-strategies.md). + + _Note: The Emission Admin is an address which has access to manage and configure the reward emissions by calling the Emission Manager contract and the general type of Transfer Strategy contract used for Liquidity Mining is of type PullRewardsStrategy._ + +3. Finally we need to configure the Liquidity Mining emissions on the Emission Manager contract from the Emission Admin by calling the `configureAssets()` function which will take the array of the following struct to configure liquidity mining for mulitple assets for the same reward or multiple assets for mutiple rewards. + + ``` + EMISSION_MANAGER.configureAssets([{ + + emissionPerSecond: The emission per second following rewards unit decimals. + + totalSupply: The total supply of the asset to incentivize. This should be kept as 0 as the Emissions Manager will fill this up. + + distributionEnd: The end of the distribution of rewards (in seconds). + + asset: The asset for which rewards should be given. Should be the address of the aave aToken (for deposit) or debtToken (for borrow). + In case where the asset for reward is for debt token please put the address of stable debt token for rewards in stable borrow mode + and address of variable debt token for rewards in variable borrow mode. + + reward: The reward token address to be used for Liquidity Mining for the asset. + + transferStrategy: The address of transfer strategy contract. + + rewardOracle: The Chainlink Aggregator compatible Price Oracle of the reward (used on off-chain infra like UI for price conversion). + + }]) + ``` + +Below is an example with the pseudo code to activate Liquidity Mining for the variable borrow of `wMatic` with `MaticX` as the reward token for the total amount of `60,000` `MaticX` for the total duration of `6 months`. For a more detailed explanation checkout this [test](./tests/EmissionTestMATICXPolygon.t.sol). + +1. Make sure the Rewards Vault has sufficient balance of the MaticX token. + + ``` + IERC20(MATIC_X_ADDRESS).balanceOf(REWARDS_VAULT) > 60000 *1e18 + ``` + +2. Do an ERC-20 approve from the MaticX token from the Rewards Vault to the transfer strategy contract for the total amount. + + ``` + IERC20(MATIC_X_ADDRESS).approve(TRANSFER_STRATEGY_ADDRESS, 60000 *1e18); + ``` + +3. Configure the Liquidity Mining emissions on the Emission Manager contract. + + ``` + EMISSION_MANAGER.configureAssets([{ + + emissionPerSecond: 60000 * 1e18 / (180 days in seconds) + + totalSupply: 0 + + distributionEnd: current timestamp + (180 days in seconds) + + asset: Aave Variable Debt Token of wMatic // 0x4a1c3aD6Ed28a636ee1751C69071f6be75DEb8B8 + + reward: MaticX Token address // 0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6 + + transferStrategy: ITransferStrategyBase(STRATEGY_ADDRESS) // 0x53F57eAAD604307889D87b747Fc67ea9DE430B01 + + rewardOracle: IEACAggregatorProxy(MaticX_ORACLE_ADDRESS) // 0x5d37E4b374E6907de8Fc7fb33EE3b0af403C7403 + + }]) + ``` ## How to modify emissions of the LM program? @@ -12,6 +85,58 @@ The emissions can be modified there, with the only requirement being that `sum(a You can run the test via `forge test -vv` which will emit the selector encoded calldata for `configureAssets` on the emission admin which you can use to execute the configuration changes e.g. via Safe. +_Note: The test example above uses total distribution and duration distribution just for convenience to define emissions per second, in reality as we only pass emissions per second to `configureAssets()` we could define it in any way we wish._ + +## How to configure emissions after the LM program has been created? + +After the LM program has been created, the emissions per second and the distribution end could be changed later on by the emissions admin to reduce the LM rewards or change the end date for the distribution. This can be done by calling `setEmissionPerSecond()` and `setDistributionEnd()` on the Emission Manager contract. The test examples on [EmissionConfigurationTestMATICXPolygon.t.sol](./tests/EmissionConfigurationTestMATICXPolygon.t.sol) shows how to do so. + +The function `_getNewEmissionPerSecond()` and `_getNewDistributionEnd()` defines the new emissions per second and new distribution end for the particular case, which could be modified there to change to modified emissions per second and distribution end. + +Similarly you can also run the test via `forge test -vv` which will emit the selector encoded calldata for `setEmissionPerSecond` and `setDistributionEnd` which can be used to make the configuration changes. + +## FAQ's: + +- Do we need to have and approve the whole liquidity mining reward initially? + + It is generally advisable to have and approve funds for the duration of the next 3 months of the Liquidity Mining Program. However it is the choice of the Emission Admin to do it progressively as well, as the users accrue rewards over time. + +- Can we configure mutiple rewards for the same asset? + + Yes, Liquidity Mining could be configured for multiple rewards for the same asset. + +- Why do we need to approve funds from the Rewards Vault to the Aave Transfer Strategy contract? + + This is needed so the Transfer Strategy contract can pull the rewards from the Rewards Vault to distribute it to the user when the user claims them. + +- Can I reuse an already deployed transfer strategy? + + Yes, a transfer strategy could be reused if it has already been deployed for the given network (given that you want the rewards vault, rewards admin and the incentives controller to be the same). + +- If a transfer strategy does not exist, how do I create one? + + The transfer strategy is an immutable contract which determines the logic of the rewards transfer. To create a new pull reward transfer strategy (most common transfer strategy for liquidity mining) you could use the +[PullRewardsTransferStrategy.sol](https://github.com/aave/aave-v3-periphery/blob/master/contracts/rewards/transfer-strategies/PullRewardsTransferStrategy.sol) contract with the following constructor params: + + - `incentivesController`: address of the incentives controller + - `rewardsAdmin`: address of the incentives controller for access control + - `rewardsVault`: address of the rewards vault containing the funds for the Liquidity Mining program. + + Example to deploy a transfer strategy can be found [here](./scripts/RewardsConfigHelpers.s.sol). + + _Note: All transfer strategy should inherit from the base contract [TransferStrategyBase.sol](https://github.com/aave/aave-v3-periphery/blob/master/contracts/rewards/transfer-strategies/TransferStrategyBase.sol) and you could also define your own custom transfer strategy even with NFT’s as rewards, given that you inherit from the base contract._ + +- Can we stop the liquidity mining program at any time? + + Yes, the liquidity mining program could be stopped at any moment by the Emission Admin. + The duration of the Liquidity Mining program could be increased as well, totally the choice of Emission Admin. + To stop the liquidity mining, we can either set the emissions per second to 0 or set the distribution end to the block we wish to stop liquiditiy mining at. + +- Can we change the amount of liquidty mining rewards? + + Yes, the liquidity mining rewards could be increased or decreased by the Emission Admin. To do so, please refer +[here](https://github.com/bgd-labs/example-liquidity-mining-aave-v3/tree/feat/configure-emissions#how-to-configure-emissions-after-the-lm-program-has-been-created) + ### Setup ```sh diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8418297 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,167 @@ +{ + "name": "example-liquidity-mining-aave-v3", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "example-liquidity-mining-aave-v3", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "prettier": "^2.8.3", + "prettier-plugin-solidity": "^1.1.1" + } + }, + "node_modules/@solidity-parser/parser": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz", + "integrity": "sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==", + "dev": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-solidity": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz", + "integrity": "sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.16.0", + "semver": "^7.3.8", + "solidity-comments-extractor": "^0.0.7" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "prettier": ">=2.3.0 || >=3.0.0-alpha.0" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + }, + "dependencies": { + "@solidity-parser/parser": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz", + "integrity": "sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==", + "dev": true, + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "dev": true + }, + "prettier-plugin-solidity": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz", + "integrity": "sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.16.0", + "semver": "^7.3.8", + "solidity-comments-extractor": "^0.0.7" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/tests/EmissionConfigurationTestMATICXPolygon.t.sol b/tests/EmissionConfigurationTestMATICXPolygon.t.sol new file mode 100644 index 0000000..c9ca45b --- /dev/null +++ b/tests/EmissionConfigurationTestMATICXPolygon.t.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {IERC20} from 'forge-std/interfaces/IERC20.sol'; +import {AaveV3Polygon, AaveV3PolygonAssets} from 'aave-address-book/AaveV3Polygon.sol'; +import {IAaveIncentivesController} from '../src/interfaces/IAaveIncentivesController.sol'; +import {IEmissionManager} from '../src/interfaces/IEmissionManager.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract EmissionConfigurationTestMATICXPolygon is BaseTest { + /// @dev Used to simplify the configuration of new emissions per second after the emissions program has been created + /// @param asset The asset for which new emissions per second needs to be configured + /// @param rewards The rewards for which new emissions per second needs to be configured + /// @param newEmissionsPerSecond The new emissions per second of the `reward` tokens + struct NewEmissionPerAsset { + address asset; + address[] rewards; + uint88[] newEmissionsPerSecond; + } + + /// @dev Used to simplify the configuration of new distribution end after the emissions program has been created + /// @param asset The asset for which new distribution end needs to be configured + /// @param reward The reward for which new distribution end needs to be configured + /// @param newDistributionEnd The new distribution end of the asset and reward + struct NewDistributionEndPerAsset { + address asset; + address reward; + uint32 newDistributionEnd; + } + + address constant EMISSION_ADMIN = 0x0c54a0BCCF5079478a144dBae1AFcb4FEdf7b263; // Polygon Foundation + address constant REWARD_ASSET = AaveV3PolygonAssets.MaticX_UNDERLYING; + + uint256 constant NEW_TOTAL_DISTRIBUTION = 30_000 ether; + uint88 constant NEW_DURATION_DISTRIBUTION_END = 15 days; + uint88 constant DURATION_DISTRIBUTION = 180 days; + + address vWMATIC_WHALE = 0xe52F5349153b8eb3B89675AF45aC7502C4997E6A; + + function setUp() public { + // For this block LM for MATICX has already been initialized + vm.createSelectFork(vm.rpcUrl('polygon'), 41047588); + } + + function test_setNewEmissionPerSecond() public { + NewEmissionPerAsset memory newEmissionPerAsset = _getNewEmissionPerSecond(); + + vm.startPrank(EMISSION_ADMIN); + + // The emission admin can change the emission per second of the reward after the rewards have been configured. + // Here we change the initial emission per second to the new one. + IEmissionManager(AaveV3Polygon.EMISSION_MANAGER).setEmissionPerSecond( + newEmissionPerAsset.asset, + newEmissionPerAsset.rewards, + newEmissionPerAsset.newEmissionsPerSecond + ); + emit log_named_bytes( + 'calldata to execute tx on EMISSION_MANAGER to set the new emission per second from the emissions admin (safe)', + abi.encodeWithSelector( + IEmissionManager.setEmissionPerSecond.selector, + newEmissionPerAsset.asset, + newEmissionPerAsset.rewards, + newEmissionPerAsset.newEmissionsPerSecond + ) + ); + + vm.stopPrank(); + + vm.warp(block.timestamp + 30 days); + + address[] memory assets = new address[](1); + assets[0] = AaveV3PolygonAssets.WMATIC_V_TOKEN; + + uint256 balanceBefore = IERC20(REWARD_ASSET).balanceOf(vWMATIC_WHALE); + + vm.startPrank(vWMATIC_WHALE); + + IAaveIncentivesController(AaveV3Polygon.DEFAULT_INCENTIVES_CONTROLLER).claimRewards( + assets, + type(uint256).max, + vWMATIC_WHALE, + REWARD_ASSET + ); + + vm.stopPrank(); + + uint256 balanceAfter = IERC20(REWARD_ASSET).balanceOf(vWMATIC_WHALE); + + // Approx estimated rewards with current emission in 1 month, considering the new emissions per second set. + uint256 deviationAccepted = 650 ether; + assertApproxEqAbs( + balanceBefore, + balanceAfter, + deviationAccepted, + 'Invalid delta on claimed rewards' + ); + } + + function test_setNewDistributionEnd() public { + NewDistributionEndPerAsset memory newDistributionEndPerAsset = _getNewDistributionEnd(); + + vm.startPrank(EMISSION_ADMIN); + + IEmissionManager(AaveV3Polygon.EMISSION_MANAGER).setDistributionEnd( + newDistributionEndPerAsset.asset, + newDistributionEndPerAsset.reward, + newDistributionEndPerAsset.newDistributionEnd + ); + emit log_named_bytes( + 'calldata to execute tx on EMISSION_MANAGER to set the new distribution end from the emissions admin (safe)', + abi.encodeWithSelector( + IEmissionManager.setDistributionEnd.selector, + newDistributionEndPerAsset.asset, + newDistributionEndPerAsset.reward, + newDistributionEndPerAsset.newDistributionEnd + ) + ); + + vm.stopPrank(); + + vm.warp(block.timestamp + 30 days); + + address[] memory assets = new address[](1); + assets[0] = AaveV3PolygonAssets.WMATIC_V_TOKEN; + + uint256 balanceBefore = IERC20(REWARD_ASSET).balanceOf(vWMATIC_WHALE); + + vm.startPrank(vWMATIC_WHALE); + + IAaveIncentivesController(AaveV3Polygon.DEFAULT_INCENTIVES_CONTROLLER).claimRewards( + assets, + type(uint256).max, + vWMATIC_WHALE, + REWARD_ASSET + ); + + vm.stopPrank(); + + uint256 balanceAfter = IERC20(REWARD_ASSET).balanceOf(vWMATIC_WHALE); + + // Approx estimated rewards with current emission in 15 days, as we changed the distribution end. + uint256 deviationAccepted = 650 ether; + assertApproxEqAbs( + balanceBefore, + balanceAfter, + deviationAccepted, + 'Invalid delta on claimed rewards' + ); + } + + function _getNewEmissionPerSecond() internal pure returns (NewEmissionPerAsset memory) { + NewEmissionPerAsset memory newEmissionPerAsset; + + address[] memory rewards = new address[](1); + rewards[0] = REWARD_ASSET; + uint88[] memory newEmissionsPerSecond = new uint88[](1); + newEmissionsPerSecond[0] = _toUint88(NEW_TOTAL_DISTRIBUTION / DURATION_DISTRIBUTION); + + newEmissionPerAsset.asset = AaveV3PolygonAssets.WMATIC_V_TOKEN; + newEmissionPerAsset.rewards = rewards; + newEmissionPerAsset.newEmissionsPerSecond = newEmissionsPerSecond; + + return newEmissionPerAsset; + } + + function _getNewDistributionEnd() internal view returns (NewDistributionEndPerAsset memory) { + NewDistributionEndPerAsset memory newDistributionEndPerAsset; + + newDistributionEndPerAsset.asset = AaveV3PolygonAssets.WMATIC_V_TOKEN; + newDistributionEndPerAsset.reward = REWARD_ASSET; + newDistributionEndPerAsset.newDistributionEnd = _toUint32( + block.timestamp + NEW_DURATION_DISTRIBUTION_END + ); + + return newDistributionEndPerAsset; + } + + function _toUint88(uint256 value) internal pure returns (uint88) { + require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); + return uint88(value); + } + + function _toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..16abce7 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,53 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@solidity-parser/parser@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.0.tgz#1fb418c816ca1fc3a1e94b08bcfe623ec4e1add4" + integrity sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +prettier-plugin-solidity@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz#9a35124f578404caf617634a8cab80862d726cba" + integrity sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg== + dependencies: + "@solidity-parser/parser" "^0.16.0" + semver "^7.3.8" + solidity-comments-extractor "^0.0.7" + +prettier@^2.8.3: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +semver@^7.3.8: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== + dependencies: + lru-cache "^6.0.0" + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==