diff --git a/README.md b/README.md index c64c0ca9..273e49d7 100644 --- a/README.md +++ b/README.md @@ -72,15 +72,22 @@ In order to improve UX, though, a frontend will be offered. In order to improve Several of the contracts implement [EIP-2771](https://eips.ethereum.org/EIPS/eip-2771), and therefore use a trusted forwarder. The forwarder will be set in the constructor and there is no way to change it after deployment. The forwarder used will be the openGSN v2 forwarder deployed on mainnet. Some information about this contract: -- [Documentation and addresses](https://docs-v2.opengsn.org/networks/ethereum/mainnet.html) +- [Documentation and addresses](https://docs-v2.opengsn.org/) - [Code](https://github.com/opengsn/gsn/blob/v2.2.5/packages/contracts/src/forwarder/Forwarder.sol) - [Audit reports](https://docs-v2.opengsn.org/audits.html) -- Deployment: **0xAa3E82b4c4093b4bA13Cb5714382C99ADBf750cA** +- Deployment on Ethereum Mainnet: **0xAa3E82b4c4093b4bA13Cb5714382C99ADBf750cA** + - deployed 2022-04-21 + - [official resources](https://docs-v2.opengsn.org/networks/xdai/xdai.html) - Visit on [etherscan](https://etherscan.io/address/0xaa3e82b4c4093b4ba13cb5714382c99adbf750ca) ([see transactions here](https://etherscan.io/txsInternal?a=0xAa3E82b4c4093b4bA13Cb5714382C99ADBf750cA&&m=advanced&p=1)) - This [dashboard](https://dune.com/oren/meta-transactions-on-ethereum-over-time) lists the forwarder as second most active forwarder contract with over 2000 transactions executed - used in our [tests](./test/CrowdinvestingERC2771.t.sol). +- Deployment on Gnosis Chain: **0x7eEae829DF28F9Ce522274D5771A6Be91d00E5ED** + - deployed 2021-04-29 + - [official resources](https://docs-v2.opengsn.org/networks/xdai/xdai.html) + - visit on [blockscout](https://gnosis.blockscout.com/address/0x7eEae829DF28F9Ce522274D5771A6Be91d00E5ED) + The platform will maintain a hot wallet (EOA) in order to send transactions to the forwarder contract. This results in the following flow: - contract A supports EIP-2771 and uses `forwarder` as its (one and only, immutable) trusted forwarder diff --git a/docs/deployment.md b/docs/deployment.md index 6736a8ba..1b4e95ce 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -53,7 +53,10 @@ forge script script/DeployToken.s.sol --rpc-url $GOERLI_RPC_URL --verify --broa ## Forwarder If the forwarder has not been deployed yet, e.g. when working in a testing environment, it can be deployed like this: -`forge create node_modules/@opengsn/contracts/src/forwarder/Forwarder.sol:Forwarder --private-key $PRIVATE_KEY --rpc-url $GOERLI_RPC_URL --verify --etherscan-api-key $ETHERSCAN_API_KEY` + +``` +forge create node_modules/@opengsn/contracts/src/forwarder/Forwarder.sol:Forwarder --private-key $PRIVATE_KEY --rpc-url $GOERLI_RPC_URL --verify --etherscan-api-key $ETHERSCAN_API_KEY +``` ## Contract Verification @@ -120,3 +123,27 @@ Provide the constructor arguments separated by whitespace in a file like this: ``` More info can be found [here](https://book.getfoundry.sh/reference/forge/forge-verify-contract). + +#### Verify on Gnosis Chiado + +Use these settings for the Chiado network: + +``` +forge [... deployment commands ...] --rpc-url $CHIADO_RPC_URL --verify --verifier blockscout --verifier-url https://gnosis-chiado.blockscout.com/api? +``` + +#### Verify on Gnosis Mainnet + +- blockscout + + ``` + forge script script/DeployPlatform.s.sol:DeployPlatform --private-key $PRIVATE_KEY --rpc-url $GNOSIS_RPC_URL --verify --verifier blockscout --verifier-url https://gnosis.blockscout.com/api? --resume + ``` + + Note that the URL provided in the output will point to gnosisscan, but that is wrong. The verification only happens on blockscout. + +- gnosisscan (requires a gnosisscan api key): + + ``` + forge script script/DeployPlatform.s.sol:DeployPlatform --private-key $PRIVATE_KEY --rpc-url $GNOSIS_RPC_URL --verify --etherscan-api-key $GNOSISSCAN_API_KEY --resume + ``` diff --git a/docs/deployments.md b/docs/deployments.md new file mode 100644 index 00000000..3b6f2ac5 --- /dev/null +++ b/docs/deployments.md @@ -0,0 +1,47 @@ +# Deployments + +## Production + +The contracts are deployed to these production networks: + +- Ethereum Mainnet +- [Gnosis Chain](https://www.gnosis.io/) + +These are the most relevant contracts. Those that we deployed were verified on blockscout and gnosisscan, as both explorers seem to be used by the community. + +| Contract | Ethereum Mainnet | Gnosis Chain (fomerly xDai chain) | | Comment | +| -------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- | ------- | +| Forwarder | | [0x7eEae829DF28F9Ce522274D5771A6Be91d00E5ED](https://gnosis.blockscout.com/address/0x7eEae829DF28F9Ce522274D5771A6Be91d00E5ED) | These were deployed by the GSN team | +| FeeSettingsCloneFactory | | [0x2659B6c5121049626363dBc1d22f267e79a0BFDE](https://gnosisscan.io/address/0x2659b6c5121049626363dbc1d22f267e79a0bfde) | | +| AllowListCloneFactory | | [0x2b27853251fcEc49617f7a3E2C9809108CCC36d6](https://gnosisscan.io/address/0x2b27853251fcec49617f7a3e2c9809108ccc36d6) | | +| TokenProxyFactory | | [0x994257AcCF99E5995F011AB2A3025063e5367629](https://gnosisscan.io/address/0x994257accf99e5995f011ab2a3025063e5367629) | | +| VestingCloneFactory | | [0xCCC45E788bcf916b3b7cA79c2e1A1fC694aD03F7](https://gnosisscan.io/address/0xccc45e788bcf916b3b7ca79c2e1a1fc694ad03f7) | | +| PrivateOfferFactory | | [0x66330A3718F68c293046d39498EDC6a043CF7190](https://gnosisscan.io/address/0x66330a3718f68c293046d39498edc6a043cf7190) | | +| FeeSettings | | [0xFce9A1e8C063162f4F54f84ab8B2744D3Efc15A2](https://gnosisscan.io/address/0xFce9A1e8C063162f4F54f84ab8B2744D3Efc15A2) | | +| AllowList | | [0xf2c479836b1f23eBE127CFB3B6dabf535d60B6DD](https://gnosisscan.io/address/0xf2c479836b1f23ebe127cfb3b6dabf535d60b6dd) | +| CrowdinvestingCloneFactory | | [0x470586e0a7c2E641c39930B96E58E4300Be32cF3](https://gnosisscan.io/address/0x470586e0a7c2e641c39930b96e58e4300be32cf3) | | | +| monerium | | [0xcB444e90D8198415266c6a2724b7900fb12FC56E](https://gnosis.blockscout.com/token/0xcB444e90D8198415266c6a2724b7900fb12FC56E) | these were deployed by the monerium team | + +## Testing + +The contracts are deployed to these testing networks: + +- Sepolia +- [Chiado](https://docs.gnosischain.com/concepts/networks/chiado) + +| Contract | Sepolia | Chiado | | Comment | +| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ------- | +| Forwarder | | [0x2659B6c5121049626363dBc1d22f267e79a0BFDE](https://gnosis-chiado.blockscout.com/address/0x2659B6c5121049626363dBc1d22f267e79a0BFDE) | | +| FeeSettingsCloneFactory | | [0xf2c479836b1f23eBE127CFB3B6dabf535d60B6DD](https://blockscout.chiadochain.net/address/0xf2c479836b1f23ebe127cfb3b6dabf535d60b6dd) | | +| AllowListCloneFactory | | [0xCCC45E788bcf916b3b7cA79c2e1A1fC694aD03F7](https://blockscout.chiadochain.net/address/0xccc45e788bcf916b3b7ca79c2e1a1fc694ad03f7) | | +| TokenProxyFactory | | [0x470586e0a7c2E641c39930B96E58E4300Be32cF3](https://blockscout.chiadochain.net/address/0x470586e0a7c2e641c39930b96e58e4300be32cf3) | | +| VestingCloneFactory | | [0x2CC672eac7326DC0c3E19d1B313548346Eb10FD8](https://blockscout.chiadochain.net/address/0x2cc672eac7326dc0c3e19d1b313548346eb10fd8) | | +| PrivateOfferFactory | | [0x994257AcCF99E5995F011AB2A3025063e5367629](https://blockscout.chiadochain.net/address/0x994257accf99e5995f011ab2a3025063e5367629) | | +| FeeSettings | | [0xab32D71F81CB897C17C9474059466bF7e117384c](https://blockscout.chiadochain.net/address/0xab32D71F81CB897C17C9474059466bF7e117384c) | | +| AllowList | | [0x774AE1a25964A0DbA498Ff7b7B59B2877B0F5be6](https://blockscout.chiadochain.net/address/0x774ae1a25964a0dba498ff7b7b59b2877b0f5be6) | +| CrowdinvestingCloneFactory | | [0x53B5E6Acd59021E61495AbD30796b09A25c880eD](https://blockscout.chiadochain.net/address/0x53b5e6acd59021e61495abd30796b09a25c880ed) | | +| tokenize.it_USDC | | [0xC3Ea9c8BF307c7022670C88dF0357E28DA975267](https://blockscout.chiadochain.net/address/0xc3ea9c8bf307c7022670c88df0357e28da975267) | | +| tokenize.it_EUROC | | [0x730653cEB98334937431e27be111369a90B9aCc7](https://blockscout.chiadochain.net/address/0x730653ceb98334937431e27be111369a90b9acc7) | | +| monerium | | [0xb106ed7587365a16b6691a3D4B2A734f4E8268a2](https://gnosis-chiado.blockscout.com/address/0xb106ed7587365a16b6691a3D4B2A734f4E8268a2) | these were deployed by the monerium team | + +On chiado, the platform cold wallet is a simple EOA with this address: 0x99b686F27413fd41F338aDD3Bce2eb60bBa7885E. diff --git a/foundry.toml b/foundry.toml index f8b3a20c..8a22e252 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,4 +14,6 @@ optimizer_runs = 10_000 gas_reports = ["AllowList", "Crowdinvesting", "FeeSettings", "PrivateOffer", "PrivateOfferFactory", "Token"] [rpc_endpoints] -goerli = "${GOERLI_RPC_URL}" \ No newline at end of file +goerli = "${GOERLI_RPC_URL}" +chiado = "${CHIADO_RPC_URL}" +gnosis = "${GNOSIS_RPC_URL}" \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 18a1e66d..a0dfd187 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -55,8 +55,13 @@ const config: HardhatUserConfig = { localhost: { url: 'http://localhost:8545', }, - ropsten: { - url: process.env.ROPSTEN_URL || '', + chiado: { + url: process.env.CHIADO_RPC_URL || '', + accounts: + process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + }, + gnosis: { + url: process.env.GNOSIS_RPC_URL || '', accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, diff --git a/package.json b/package.json index fd131639..cefd55cf 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,8 @@ }, "dependencies": { "@opengsn/contracts": "2.2.5", - "@openzeppelin/contracts": "4.9.5", - "@openzeppelin/contracts-upgradeable": "4.9.5" + "@openzeppelin/contracts": "4.9.6", + "@openzeppelin/contracts-upgradeable": "4.9.6" }, "scripts": { "prepack": "yarn npmignore --auto && yarn test && yarn build ", diff --git a/script/DeployPlatform.s.sol b/script/DeployPlatform.s.sol index 98bfcac9..d451efdc 100644 --- a/script/DeployPlatform.s.sol +++ b/script/DeployPlatform.s.sol @@ -9,6 +9,7 @@ import "../contracts/factories/AllowListCloneFactory.sol"; import "../contracts/factories/PrivateOfferFactory.sol"; import "../contracts/factories/VestingCloneFactory.sol"; import "../contracts/factories/TokenProxyFactory.sol"; +import "../contracts/factories/CrowdinvestingCloneFactory.sol"; import "@opengsn/contracts/src/forwarder/Forwarder.sol"; contract DeployPlatform is Script { @@ -17,7 +18,6 @@ contract DeployPlatform is Script { function run() public { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address deployerAddress = vm.addr(deployerPrivateKey); - address platformColdWallet = 0x9E23f8AA17B2721cf69D157b8a15bd7b64ac881C; // Sepolia and Mainnet /* * config @@ -35,73 +35,87 @@ contract DeployPlatform is Script { // trustedCurrencies[5] = address(0xcB444e90D8198415266c6a2724b7900fb12FC56E); // EURe // Mainnet - address trustedForwarder = 0xAa3E82b4c4093b4bA13Cb5714382C99ADBf750cA; - address[] memory trustedCurrencies = new address[](6); - trustedCurrencies[0] = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // WEth - trustedCurrencies[1] = address(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); // WBTC - trustedCurrencies[2] = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); // USDC - trustedCurrencies[3] = address(0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c); // EUROC - trustedCurrencies[4] = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); // DAI - trustedCurrencies[5] = address(0x3231Cb76718CDeF2155FC47b5286d82e6eDA273f); // EURe + // address platformColdWallet = 0x9E23f8AA17B2721cf69D157b8a15bd7b64ac881C; + // address trustedForwarder = 0xAa3E82b4c4093b4bA13Cb5714382C99ADBf750cA; + // address[] memory trustedCurrencies = new address[](6); + // trustedCurrencies[0] = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // WEth + // trustedCurrencies[1] = address(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); // WBTC + // trustedCurrencies[2] = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); // USDC + // trustedCurrencies[3] = address(0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c); // EUROC + // trustedCurrencies[4] = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); // DAI + // trustedCurrencies[5] = address(0x3231Cb76718CDeF2155FC47b5286d82e6eDA273f); // EURe // Sepolia + // address platformColdWallet = 0x9E23f8AA17B2721cf69D157b8a15bd7b64ac881C; // address trustedForwarder = 0x274ca5f21Cdde06B6E4Fe063f5087EB6Cf3eAe55; // address[] memory trustedCurrencies = new address[](2); // trustedCurrencies[0] = address(0x30627856Ef668F0A6a1ca9145C9538f7d5b42BDE); // tEUROC // trustedCurrencies[1] = address(0x86f488C7CC923d987b246994a0E5e20B3364fd92); // tUSDC + // Gnosis Chiado + // address platformColdWallet = 0x99b686F27413fd41F338aDD3Bce2eb60bBa7885E; + // address trustedForwarder = 0x2659B6c5121049626363dBc1d22f267e79a0BFDE; + // address[] memory trustedCurrencies = new address[](6); + // trustedCurrencies[0] = address(0xb106ed7587365a16b6691a3D4B2A734f4E8268a2); // Monerium + + // Gnosis Mainnet + address platformColdWallet = 0x9E23f8AA17B2721cf69D157b8a15bd7b64ac881C; + address trustedForwarder = 0x7eEae829DF28F9Ce522274D5771A6Be91d00E5ED; + address[] memory trustedCurrencies = new address[](6); + trustedCurrencies[0] = address(0xcB444e90D8198415266c6a2724b7900fb12FC56E); // Monerium + /* * execution */ console.log("Deployer address: ", deployerAddress); vm.startBroadcast(deployerPrivateKey); - // console.log("Deploying FeeSettingsCloneFactory contract..."); - // FeeSettings feeSettingsLogicContract = new FeeSettings(trustedForwarder); - // FeeSettingsCloneFactory feeSettingsCloneFactory = new FeeSettingsCloneFactory( - // address(feeSettingsLogicContract) - // ); - // console.log("FeeSettingsCloneFactory deployed at: ", address(feeSettingsCloneFactory)); - - // console.log("Deploying FeeSettings contract..."); - // Fees memory fees = Fees(200, 600, 200, 0); - // FeeSettings feeSettings = FeeSettings( - // feeSettingsCloneFactory.createFeeSettingsClone( - // bytes32(0), - // trustedForwarder, - // platformColdWallet, - // fees, - // platformColdWallet, - // platformColdWallet, - // platformColdWallet - // ) - // ); - // console.log("FeeSettings deployed at: ", address(feeSettings)); - - // console.log("Deploying AllowListCloneFactory contract..."); - // AllowList allowListLogicContract = new AllowList(trustedForwarder); - // AllowListCloneFactory allowListCloneFactory = new AllowListCloneFactory(address(allowListLogicContract)); - // console.log("AllowListCloneFactory deployed at: ", address(allowListCloneFactory)); - - // console.log("Deploying AllowList contract..."); - - // uint256[] memory attributes = new uint256[](trustedCurrencies.length); - // for (uint256 i = 0; i < trustedCurrencies.length; i++) { - // attributes[i] = TRUSTED_CURRENCY; - // } - // AllowList allowList = AllowList( - // allowListCloneFactory.createAllowListClone( - // bytes32(0), - // trustedForwarder, - // platformColdWallet, - // trustedCurrencies, - // attributes - // ) - // ); - // console.log("Allowlist deployed at: ", address(allowList)); + console.log("Deploying FeeSettingsCloneFactory contract..."); + FeeSettings feeSettingsLogicContract = new FeeSettings(trustedForwarder); + FeeSettingsCloneFactory feeSettingsCloneFactory = new FeeSettingsCloneFactory( + address(feeSettingsLogicContract) + ); + console.log("FeeSettingsCloneFactory deployed at: ", address(feeSettingsCloneFactory)); + + console.log("Deploying FeeSettings contract..."); + Fees memory fees = Fees(200, 600, 200, 0); + FeeSettings feeSettings = FeeSettings( + feeSettingsCloneFactory.createFeeSettingsClone( + bytes32(0), + trustedForwarder, + platformColdWallet, + fees, + platformColdWallet, + platformColdWallet, + platformColdWallet + ) + ); + console.log("FeeSettings deployed at: ", address(feeSettings)); + + console.log("Deploying AllowListCloneFactory contract..."); + AllowList allowListLogicContract = new AllowList(trustedForwarder); + AllowListCloneFactory allowListCloneFactory = new AllowListCloneFactory(address(allowListLogicContract)); + console.log("AllowListCloneFactory deployed at: ", address(allowListCloneFactory)); + + console.log("Deploying AllowList contract..."); + + uint256[] memory attributes = new uint256[](trustedCurrencies.length); + for (uint256 i = 0; i < trustedCurrencies.length; i++) { + attributes[i] = TRUSTED_CURRENCY; + } + AllowList allowList = AllowList( + allowListCloneFactory.createAllowListClone( + bytes32(0), + trustedForwarder, + platformColdWallet, + trustedCurrencies, + attributes + ) + ); + console.log("Allowlist deployed at: ", address(allowList)); console.log("Deploying VestingCloneFactory contract..."); - Vesting vestingImplementation = new Vesting(trustedForwarder); + Vesting vestingImplementation = Vesting(0xa740Dd991655b2887321C71D8Ac0c171a0C9E969); VestingCloneFactory vestingCloneFactory = new VestingCloneFactory(address(vestingImplementation)); console.log("VestingCloneFactory deployed at: ", address(vestingCloneFactory)); @@ -109,10 +123,10 @@ contract DeployPlatform is Script { PrivateOfferFactory privateOfferFactory = new PrivateOfferFactory(vestingCloneFactory); console.log("PrivateOfferFactory deployed at: ", address(privateOfferFactory)); - // console.log("Deploying TokenProxyFactory contract..."); - // Token tokenImplementation = new Token(trustedForwarder); - // TokenProxyFactory tokenProxyFactory = new TokenProxyFactory(address(tokenImplementation)); - // console.log("TokenProxyFactory deployed at: ", address(tokenProxyFactory)); + console.log("Deploying TokenProxyFactory contract..."); + Token tokenImplementation = new Token(trustedForwarder); + TokenProxyFactory tokenProxyFactory = new TokenProxyFactory(address(tokenImplementation)); + console.log("TokenProxyFactory deployed at: ", address(tokenProxyFactory)); // console.log("Deploying Tokens to use as currrency on testnet..."); // Token tUSDC = Token( @@ -143,6 +157,14 @@ contract DeployPlatform is Script { // console.log("tEUROC deployed at: ", address(tEUROC)); // console.log("Remember to add the tokens to the AllowList with attribute TRUSTED_CURRENCY: ", TRUSTED_CURRENCY); + // deploy crowdinvesting factory + console.log("Deploying CrowdinvestingFactory contract..."); + Crowdinvesting crowdinvestingImplementation = new Crowdinvesting(trustedForwarder); + CrowdinvestingCloneFactory crowdinvestingCloneFactory = new CrowdinvestingCloneFactory( + address(crowdinvestingImplementation) + ); + console.log("CrowdinvestingFactory deployed at: ", address(crowdinvestingCloneFactory)); + vm.stopBroadcast(); } } diff --git a/test/Vesting.t.sol b/test/Vesting.t.sol index 3a291f3c..b884a917 100644 --- a/test/Vesting.t.sol +++ b/test/Vesting.t.sol @@ -510,4 +510,21 @@ contract VestingTest is Test { vm.expectRevert("newStartTime must be after endTime"); vesting.pauseVesting(id, exampleStart + exampleCliff + 1, exampleStart + exampleCliff / 2); } + + /** + * this test causes a division by zero and ensures the transaction fails as it should + */ + function testReleaseWithDivBy0() public { + uint64 start = 2 * 365 days; + uint64 duration = 0; + vm.startPrank(owner); + uint64 id = vesting.createVesting(exampleAmount, beneficiary, start, duration, duration, true); + vm.stopPrank(); + + vm.warp(start); + + vm.startPrank(beneficiary); + vm.expectRevert(stdError.divisionError); + vesting.release(id); + } } diff --git a/yarn.lock b/yarn.lock index f6a72155..7e6e8011 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1068,15 +1068,15 @@ dependencies: "@openzeppelin/contracts" "^4.2.0" -"@openzeppelin/contracts-upgradeable@4.9.5": - version "4.9.5" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" - integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== - -"@openzeppelin/contracts@4.9.5": - version "4.9.5" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" - integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== +"@openzeppelin/contracts-upgradeable@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz#38b21708a719da647de4bb0e4802ee235a0d24df" + integrity sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA== + +"@openzeppelin/contracts@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" + integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA== "@openzeppelin/contracts@^4.2.0": version "4.8.3"