From d7005300937e86e0ed20480ef785ec55b17607a1 Mon Sep 17 00:00:00 2001 From: James Duncombe Date: Wed, 18 Oct 2023 09:54:29 +0100 Subject: [PATCH] wip --- contracts/common/AHasForwarder.sol | 22 +++++ contracts/common/lib/LibHasForwarder.sol | 22 +++++ contracts/fast/FastInitFacet.sol | 3 +- contracts/interfaces/ICustomErrors.sol | 2 + contracts/interfaces/IERC165.sol | 12 --- contracts/issuer/IssuerInitFacet.sol | 3 +- .../marketplace/MarketplaceInitFacet.sol | 11 ++- contracts/marketplace/lib/LibMarketplace.sol | 2 - contracts/paymaster/PaymasterInitFacet.sol | 40 ++++++++ contracts/paymaster/PaymasterTopFacet.sol | 38 ++++++++ contracts/paymaster/lib/APaymasterFacet.sol | 25 +++++ contracts/paymaster/lib/LibPaymaster.sol | 22 +++++ deploy/20_deployMarketplace.ts | 5 +- deploy/30_deployPaymaster.ts | 12 +++ hardhat.config.ts | 19 ++++ package.json | 1 + scripts/geth-local-poa-chain.sh | 3 +- tasks/marketplace.ts | 16 +++- tasks/paymaster.ts | 95 +++++++++++++++++++ yarn.lock | 9 +- 20 files changed, 337 insertions(+), 25 deletions(-) create mode 100644 contracts/common/AHasForwarder.sol create mode 100644 contracts/common/lib/LibHasForwarder.sol delete mode 100644 contracts/interfaces/IERC165.sol create mode 100644 contracts/paymaster/PaymasterInitFacet.sol create mode 100644 contracts/paymaster/PaymasterTopFacet.sol create mode 100644 contracts/paymaster/lib/APaymasterFacet.sol create mode 100644 contracts/paymaster/lib/LibPaymaster.sol create mode 100644 deploy/30_deployPaymaster.ts create mode 100644 tasks/paymaster.ts diff --git a/contracts/common/AHasForwarder.sol b/contracts/common/AHasForwarder.sol new file mode 100644 index 00000000..5b2c58e1 --- /dev/null +++ b/contracts/common/AHasForwarder.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import "../common/lib/LibHasForwarder.sol"; + +import "@opengsn/contracts/src/ERC2771Recipient.sol"; + +/** + * @title The Forwarder Smart Contract. + * @notice The HasForwarder abstract contract is in charge of... + */ +abstract contract AHasForwarder is ERC2771Recipient { + /** + * @notice ERC2771Recipient stuff... + * FIX THE MODIFIER !!!!!!!!!!!!!!!!!!!!!! + */ + function setTrustedForwarder(address _forwarderAddress) external { + LibHasForwarder.Data storage ds = LibHasForwarder.data(); + ds.forwarderAddress = _forwarderAddress; + _setTrustedForwarder(_forwarderAddress); + } +} diff --git a/contracts/common/lib/LibHasForwarder.sol b/contracts/common/lib/LibHasForwarder.sol new file mode 100644 index 00000000..8f873003 --- /dev/null +++ b/contracts/common/lib/LibHasForwarder.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +library LibHasForwarder { + // The current version of the storage. + uint16 internal constant STORAGE_VERSION = 1; + // This is `keccak256('HasForwarder.storage.Main')`. + bytes32 internal constant STORAGE_SLOT = 0xa9930c2ffa1b605b0243ba36b3020146bcba5a29c05a711f5ca7c705a8e851ca; + + struct Data { + /// @notice The latest intializer version that was called. + uint16 version; + /// @notice This is where we store the trusted forwarder address. + address forwarderAddress; + } + + function data() internal pure returns (Data storage s) { + assembly { + s.slot := STORAGE_SLOT + } + } +} diff --git a/contracts/fast/FastInitFacet.sol b/contracts/fast/FastInitFacet.sol index f607ae8f..001c844b 100644 --- a/contracts/fast/FastInitFacet.sol +++ b/contracts/fast/FastInitFacet.sol @@ -7,7 +7,6 @@ import "../common/lib/LibHasAutomatons.sol"; import "../common/AHasGovernors.sol"; import "../common/AHasMembers.sol"; import "../common/AHasAutomatons.sol"; -import "../interfaces/IERC165.sol"; // Interface Support. import "../interfaces/IERC173.sol"; // Ownership. import "../interfaces/IDiamondCut.sol"; // Facet management. import "../interfaces/IDiamondLoupe.sol"; // Facet introspection. @@ -22,6 +21,8 @@ import "./lib/LibFastHistory.sol"; import "./lib/LibFastDistributions.sol"; import "./lib/LibFastCrowdfunds.sol"; +import "@openzeppelin/contracts/interfaces/IERC165.sol"; + /** * @notice NotAlthough this contract doesn't explicitelly inherit from IERC173, ERC165, IDiamondLoupe etc, all * methods are in fact implemented by the underlaying Diamond proxy. It is therefore safe to diff --git a/contracts/interfaces/ICustomErrors.sol b/contracts/interfaces/ICustomErrors.sol index ff90222c..9e6f16b4 100644 --- a/contracts/interfaces/ICustomErrors.sol +++ b/contracts/interfaces/ICustomErrors.sol @@ -8,7 +8,9 @@ interface ICustomErrors { error InconsistentParameter(string param); error InsufficientFunds(uint256 amount); error InternalMethod(); + error InvalidApprovalDataLength(); error InvalidCrowdfundBasisPointsFee(uint32 fee); + error InvalidPaymasterDataLength(); error InvalidPhase(); error NonExistentEntry(); error OutOfBounds(); diff --git a/contracts/interfaces/IERC165.sol b/contracts/interfaces/IERC165.sol deleted file mode 100644 index e5885239..00000000 --- a/contracts/interfaces/IERC165.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.10; - -/// @title ERC165 definition - interface implementation queryability. -interface IERC165 { - /// @notice Queries if a contract implements an interface - /// @param interfaceId The interface identifier, as specified in ERC165. - /// @notice Interface identification is specified in ERC-165. This method uses less than 30,000 gas. - /// @return A `bool` set to `true` if the contract implements `interfaceID` and - /// `interfaceID` is not 0xffffffff. - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} diff --git a/contracts/issuer/IssuerInitFacet.sol b/contracts/issuer/IssuerInitFacet.sol index c17a3fde..97d7e534 100644 --- a/contracts/issuer/IssuerInitFacet.sol +++ b/contracts/issuer/IssuerInitFacet.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.10; import "../common/AHasMembers.sol"; import "../common/AHasAutomatons.sol"; -import "../interfaces/IERC165.sol"; // Interface Support. import "../interfaces/IERC173.sol"; // Ownership. import "../interfaces/IDiamondCut.sol"; // Facet management. import "../interfaces/IDiamondLoupe.sol"; // Facet introspection. @@ -14,6 +13,8 @@ import "./lib/AIssuerFacet.sol"; import "./lib/LibIssuer.sol"; import "./lib/LibIssuerAccess.sol"; +import "@openzeppelin/contracts/interfaces/IERC165.sol"; + /** * @title The Issuer Smart Contract. * @notice The marketplace contract is in charge of keeping track of marketplace members and has logic diff --git a/contracts/marketplace/MarketplaceInitFacet.sol b/contracts/marketplace/MarketplaceInitFacet.sol index 9eb6d0c9..a752f269 100644 --- a/contracts/marketplace/MarketplaceInitFacet.sol +++ b/contracts/marketplace/MarketplaceInitFacet.sol @@ -3,9 +3,10 @@ pragma solidity 0.8.10; import "../common/lib/LibHasMembers.sol"; import "../common/lib/LibHasAutomatons.sol"; +import "../common/lib/LibHasForwarder.sol"; import "../common/AHasMembers.sol"; import "../common/AHasAutomatons.sol"; -import "../interfaces/IERC165.sol"; // Interface Support. +import "../common/AHasForwarder.sol"; import "../interfaces/IERC173.sol"; // Ownership. import "../interfaces/IDiamondCut.sol"; // Facet management. import "../interfaces/IDiamondLoupe.sol"; // Facet introspection. @@ -16,12 +17,15 @@ import "./lib/LibMarketplace.sol"; import "./lib/LibMarketplaceAccess.sol"; import "./lib/LibMarketplaceTokenHolders.sol"; +import "@openzeppelin/contracts/interfaces/IERC165.sol"; + /// @notice The Marketplace initialization facet. contract MarketplaceInitFacet is AMarketplaceFacet { /// Initializers. struct InitializerParams { address issuer; + address trustedForwarder; } function initialize(InitializerParams calldata params) external onlyDeployer { @@ -61,5 +65,10 @@ contract MarketplaceInitFacet is AMarketplaceFacet { // Initialize automatons storage. LibHasAutomatons.data().version = LibHasAutomatons.STORAGE_VERSION; + + // ------------------------------------- // + + // Initialize forwarder storage. + LibHasForwarder.data().forwarderAddress = params.trustedForwarder; } } diff --git a/contracts/marketplace/lib/LibMarketplace.sol b/contracts/marketplace/lib/LibMarketplace.sol index 9eedd123..c5cf051c 100644 --- a/contracts/marketplace/lib/LibMarketplace.sol +++ b/contracts/marketplace/lib/LibMarketplace.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.10; -import "../../lib/LibAddressSet.sol"; - library LibMarketplace { // The current version of the storage. uint16 internal constant STORAGE_VERSION = 1; diff --git a/contracts/paymaster/PaymasterInitFacet.sol b/contracts/paymaster/PaymasterInitFacet.sol new file mode 100644 index 00000000..b67e07d3 --- /dev/null +++ b/contracts/paymaster/PaymasterInitFacet.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import "../interfaces/IERC173.sol"; // Ownership. +import "../interfaces/IDiamondCut.sol"; // Facet management. +import "../interfaces/IDiamondLoupe.sol"; // Facet introspection. +import "../interfaces/ICustomErrors.sol"; +import "../lib/LibDiamond.sol"; +import "./lib/APaymasterFacet.sol"; +import "./lib/LibPaymaster.sol"; + +import "@openzeppelin/contracts/interfaces/IERC165.sol"; + +/// @notice The Paymaster initialization facet. +contract PaymasterInitFacet is APaymasterFacet { + /// Initializers. + + struct InitializerParams { + address marketplace; + } + + function initialize(InitializerParams calldata params) external onlyDeployer { + // Make sure we haven't initialized yet. + if (LibPaymaster.data().version >= LibPaymaster.STORAGE_VERSION) revert ICustomErrors.AlreadyInitialized(); + + // Register interfaces. + LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); + ds.supportedInterfaces[type(IERC165).interfaceId] = true; + ds.supportedInterfaces[type(IERC173).interfaceId] = true; + ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true; + ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; + + // ------------------------------------- // + + // Initialize top-level storage. + LibPaymaster.Data storage topData = LibPaymaster.data(); + topData.version = LibPaymaster.STORAGE_VERSION; + topData.marketplace = params.marketplace; + } +} diff --git a/contracts/paymaster/PaymasterTopFacet.sol b/contracts/paymaster/PaymasterTopFacet.sol new file mode 100644 index 00000000..3d58b62c --- /dev/null +++ b/contracts/paymaster/PaymasterTopFacet.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import "./lib/LibPaymaster.sol"; +import "./lib/APaymasterFacet.sol"; + +import "@opengsn/contracts/src/BasePaymaster.sol"; + +contract PaymasterTopFacet is APaymasterFacet, BasePaymaster { + bool public useRejectOnRecipientRevert = false; + + // TODO: Do we use the Marketplace at this point to check that the sender is allowed? + function _preRelayedCall( + GsnTypes.RelayRequest calldata relayRequest, + bytes calldata signature, + bytes calldata approvalData, + uint256 maxPossibleGas + ) internal virtual override returns (bytes memory context, bool revertOnRecipientRevert) { + (signature, maxPossibleGas); + if (approvalData.length == 0) revert ICustomErrors.InvalidApprovalDataLength(); + if (relayRequest.relayData.paymasterData.length == 0) revert ICustomErrors.InvalidPaymasterDataLength(); + + return ("", useRejectOnRecipientRevert); + } + + function _postRelayedCall( + bytes calldata context, + bool success, + uint256 gasUseWithoutPost, + GsnTypes.RelayData calldata relayData + ) internal virtual override { + (context, success, gasUseWithoutPost, relayData); + } + + function versionPaymaster() external view virtual override returns (string memory) { + return "3.0.0-beta.3+opengsn.accepteverything.ipaymaster"; + } +} diff --git a/contracts/paymaster/lib/APaymasterFacet.sol b/contracts/paymaster/lib/APaymasterFacet.sol new file mode 100644 index 00000000..7878f904 --- /dev/null +++ b/contracts/paymaster/lib/APaymasterFacet.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import "../../common/AHasMembers.sol"; +import "../../interfaces/ICustomErrors.sol"; +import "../../lib/LibHelpers.sol"; +import "../lib/LibPaymaster.sol"; + +/** + * @notice This contract is a group of modifiers that can be used by any Paymaster facets to guard against + * certain permissions. + */ +abstract contract APaymasterFacet { + /// Internal ACL functions. + + /// ... + + // Modifiers. + + /// @notice Ensures that a method can only be called by the singleton deployer contract factory. + modifier onlyDeployer() virtual { + if (!LibHelpers._isDeployer(msg.sender)) revert ICustomErrors.InternalMethod(); + _; + } +} diff --git a/contracts/paymaster/lib/LibPaymaster.sol b/contracts/paymaster/lib/LibPaymaster.sol new file mode 100644 index 00000000..2aa2034f --- /dev/null +++ b/contracts/paymaster/lib/LibPaymaster.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +library LibPaymaster { + // The current version of the storage. + uint16 internal constant STORAGE_VERSION = 1; + // This is keccak256('Paymaster.storage'): + bytes32 internal constant STORAGE_SLOT = 0x8f0e66ee30211ca069424cd4b533ee66f04c45421216c1a6601cf23359c1f7f8; + + struct Data { + /// @notice The latest intializer version that was called. + uint16 version; + /// @notice The internal pointer to the Marketplace contract. + address marketplace; + } + + function data() internal pure returns (Data storage s) { + assembly { + s.slot := STORAGE_SLOT + } + } +} diff --git a/deploy/20_deployMarketplace.ts b/deploy/20_deployMarketplace.ts index 53cb67c9..dec68ce9 100644 --- a/deploy/20_deployMarketplace.ts +++ b/deploy/20_deployMarketplace.ts @@ -6,7 +6,10 @@ import { deployments } from "hardhat"; const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { console.log("----------------------------------- 20_deployMarketplace"); - await deployMarketplace(hre, (await deployments.get("Issuer")).address); + // TODO: From a lookup... + const trustedForwarderAddr = "0xB2b5841DBeF766d4b521221732F9B618fCf34A87"; + + await deployMarketplace(hre, (await deployments.get("Issuer")).address, trustedForwarderAddr); }; func.tags = ["DeployMarketplace"]; export default func; diff --git a/deploy/30_deployPaymaster.ts b/deploy/30_deployPaymaster.ts new file mode 100644 index 00000000..0a680beb --- /dev/null +++ b/deploy/30_deployPaymaster.ts @@ -0,0 +1,12 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { deployPaymaster } from "../tasks/paymaster"; +import { deployments } from "hardhat"; + +const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + console.log("----------------------------------- 30_deployPaymaster"); + + await deployPaymaster(hre, (await deployments.get("Marketplace")).address); +}; +func.tags = ["DeployPaymaster"]; +export default func; diff --git a/hardhat.config.ts b/hardhat.config.ts index b442c266..8603ade1 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -10,11 +10,13 @@ import { DEPLOYER_FACTORY_COMMON, accounts, abiFilter } from "./src/utils"; import { ISSUER_FACETS } from "./tasks/issuer"; import { MARKETPLACE_FACETS } from "./tasks/marketplace"; import { FAST_FACETS } from "./tasks/fast"; +import { PAYMASTER_FACETS } from "./tasks/paymaster"; // Import all of our tasks here! import "./tasks/accounts"; import "./tasks/issuer"; import "./tasks/marketplace"; +import "./tasks/paymaster"; import "./tasks/fast"; import "./tasks/upgrades"; @@ -121,6 +123,23 @@ const config: HardhatUserConfig = { ...FAST_FACETS, ], }, + { + name: "Paymaster", + filter: abiFilter([ + // Event types. + // ... + // Error types. + ["Facet$", "InvalidApprovalDataLength()"], + ["Facet$", "InvalidPaymasterDataLength()"], + ]), + include: [ + // "IERC165", + // "IERC173", + "IDiamondCut", + "IDiamondLoupe", + ...PAYMASTER_FACETS, + ], + }, ], networks: { // Built-in for tests etc. diff --git a/package.json b/package.json index 57ca132e..bbbddd6c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@openzeppelin/contracts": "^4.9.2", + "@opengsn/contracts": "^3.0.0-beta.6", "@typechain/ethers-v5": "^11.1.1", "@typechain/hardhat": "^9.0.0", "ethers": "^5.7.2", diff --git a/scripts/geth-local-poa-chain.sh b/scripts/geth-local-poa-chain.sh index 5cbbd39c..184db551 100755 --- a/scripts/geth-local-poa-chain.sh +++ b/scripts/geth-local-poa-chain.sh @@ -24,4 +24,5 @@ geth --dev \ --http.addr "0.0.0.0" \ --http.port 8546 \ --http.api "eth,web3,net" \ - --http.corsdomain "*" + --http.corsdomain "*" \ + --ipcdisable diff --git a/tasks/marketplace.ts b/tasks/marketplace.ts index 52322664..183ca455 100644 --- a/tasks/marketplace.ts +++ b/tasks/marketplace.ts @@ -6,16 +6,18 @@ import { Marketplace } from "../typechain/hardhat-diamond-abi/HardhatDiamondABI. // Tasks. -interface MarketplaceDeployParams {} +interface MarketplaceDeployParams { } task("marketplace-deploy", "Deploys the main Marketplace contract").setAction( async (_params: MarketplaceDeployParams, hre) => { const { address: issuerAddr } = await hre.deployments.get("Issuer"); - await deployMarketplace(hre, issuerAddr); + // TODO: From a lookup... + const trustedForwarderAddr = "0xB2b5841DBeF766d4b521221732F9B618fCf34A87"; + await deployMarketplace(hre, issuerAddr, trustedForwarderAddr); } ); -interface MarketplaceUpdateFacetsParams {} +interface MarketplaceUpdateFacetsParams { } task( "marketplace-update-facets", @@ -62,7 +64,8 @@ const MARKETPLACE_FACETS = [ const deployMarketplace = async ( hre: HardhatRuntimeEnvironment, - issuerAddr: string + issuerAddr: string, + trustedForwarderAddr: string ): Promise => { const { ethers, deployments, getNamedAccounts } = hre; const { diamond } = deployments; @@ -80,7 +83,10 @@ const deployMarketplace = async ( execute: { contract: "MarketplaceInitFacet", methodName: "initialize", - args: [{ issuer: issuerAddr }], + args: [{ + issuer: issuerAddr, + trustedForwarder: trustedForwarderAddr + }], }, deterministicSalt: deploymentSalt(hre), log: true, diff --git a/tasks/paymaster.ts b/tasks/paymaster.ts new file mode 100644 index 00000000..16f07d37 --- /dev/null +++ b/tasks/paymaster.ts @@ -0,0 +1,95 @@ +import { task } from "hardhat/config"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { parseEther } from "ethers/lib/utils"; +import { deploymentSalt } from "../src/utils"; +import { Paymaster } from "../typechain/hardhat-diamond-abi/HardhatDiamondABI.sol"; + +// Tasks. + +interface PaymasterDeployParams { } + +task("paymaster-deploy", "Deploys the main Paymaster contract").setAction( + async (_params: PaymasterDeployParams, hre) => { + const { address: marketplaceAddr } = await hre.deployments.get("Marketplace"); + await deployPaymaster(hre, marketplaceAddr); + } +); + +interface PaymasterUpdateFacetsParams { } + +task("paymaster-update-facets", "Updates facets of our Paymaster") + .setAction(async (_params: PaymasterUpdateFacetsParams, hre) => { + const { deployments, getNamedAccounts } = hre; + const { deployer } = await getNamedAccounts(); + // Make sure that the fast is known from our tooling. + const { address } = await deployments.get("Paymaster"); + console.log(`Updating paymaster diamond facets at ${address}...`); + await deployments.diamond.deploy("Paymaster", { + from: deployer, + facets: PAYMASTER_FACETS, + deterministicSalt: deploymentSalt(hre), + log: true, + }); + }); + + +interface PaymasterFundParams { } + +task("paymaster-fund", "Funds the Paymaster") + .setAction(async (_params: PaymasterFundParams, hre) => { + const { deployments, getNamedAccounts } = hre; + // Who will juice it? + const { issuerMember } = await getNamedAccounts(); + + // yeeettttttt this out... + const relayHubAddress = "0x3232f21A6E08312654270c78A773f00dd61d60f5"; + const { address: paymasterAddress } = await deployments.get("Paymaster"); + + const RelayHub = await hre.ethers.getContractFactory("RelayHub"); + const relayHub = await RelayHub.attach(relayHubAddress); + + // params... + const tx = await relayHub.depositFor(paymasterAddress, { value: parseEther("0.1") }); + await tx.wait(); + + console.log('Paymaster balance:', await relayHub.balanceOf(paymasterAddress)); + // console.log('Admin wallet balance', await provider.getBalance(admin.address)); + }); + +// Reusable functions and constants. + +const PAYMASTER_FACETS = [ + "PaymasterTopFacet" +]; + +const deployPaymaster = async ( + hre: HardhatRuntimeEnvironment, + marketplaceAddr: string +): Promise => { + const { ethers, deployments, getNamedAccounts } = hre; + const { diamond } = deployments; + const { deployer } = await getNamedAccounts(); + + let deploy = await deployments.getOrNull("Paymaster"); + if (deploy) { + console.log(`Paymaster already deployed at ${deploy.address}, skipping.`); + } else { + // Deploy the diamond with an additional initialization facet. + deploy = await diamond.deploy("Paymaster", { + from: deployer, + owner: deployer, + facets: PAYMASTER_FACETS, + execute: { + contract: "PaymasterInitFacet", + methodName: "initialize", + args: [{ marketplace: marketplaceAddr }], + }, + deterministicSalt: deploymentSalt(hre), + log: true, + }); + } + // Return a handle to the diamond. + return await ethers.getContract("Paymaster"); +}; + +export { PAYMASTER_FACETS, deployPaymaster }; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 78f6d3c1..58d4f8a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2045,7 +2045,14 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.6.tgz#d11cb063a5f61a77806053e54009c40ddee49a54" integrity sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg== -"@openzeppelin/contracts@^4.9.2": +"@opengsn/contracts@^3.0.0-beta.6": + version "3.0.0-beta.6" + resolved "https://registry.yarnpkg.com/@opengsn/contracts/-/contracts-3.0.0-beta.6.tgz#3455b7c249922a087422721689239a7d539d1138" + integrity sha512-zqd7BG339Uq5bfx40CwKXr2waaJqLN9qBVllcWhKBGnkZPKR59wVIPh2zTNzvLorndMRWB3TnOQxAr6tZKjwGQ== + dependencies: + "@openzeppelin/contracts" "^4.2.0" + +"@openzeppelin/contracts@^4.2.0", "@openzeppelin/contracts@^4.9.2": version "4.9.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364" integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==