diff --git a/.changeset/clever-monkeys-taste.md b/.changeset/clever-monkeys-taste.md new file mode 100644 index 00000000..eaf6b971 --- /dev/null +++ b/.changeset/clever-monkeys-taste.md @@ -0,0 +1,78 @@ +--- +'rankify-contracts': minor +--- + +# Changeset for branch 64-principal-game-cost-time-parameters + +## Summary +This branch introduces significant changes to game cost and time parameters, payment handling, and rank token mechanics, along with several code improvements and bug fixes. + +## Changes + +### Core Game Mechanics +- `LibRankify.sol`: + - Introduced principal game cost calculation based on game time + - Added minimum game time validation and constraints + - Implemented 90/10 payment split: 90% burned, 10% to DAO + - Removed payment refunds and game cancellation payments + - Simplified rank token rewards to only top player + - Added validation for turn count and game time parameters + +### Libraries +- `LibTBG.sol`: + - Added `startedAt` timestamp for minimum game time tracking + - Renamed `getGameSettings()` to `getSettings(uint256 gameId)` for better clarity + - Updated storage access patterns for overtime functionality + - Simplified tie detection logic to only consider top 2 players + - Fixed storage slot access patterns + +### Tokens +- `DistributableGovernanceERC20.sol`: + - Updated Solidity version from 0.8.20 to 0.8.28 + +- `RankToken.sol`: + - Updated Solidity version from ^0.8.20 to =0.8.28 + - Added IERC165 import + - Implemented burn function with ERC7746C middleware + +### Vendor +- Renamed `DiamondCloneable.sol` to `DiamondClonable.sol`: + - Fixed typo in error name from 'fucntionDoesNotExist' to 'functionDoesNotExist' + +- `DiamondLoupeFacet.sol`: + - Updated Solidity version to ^0.8.28 + +- `LibDiamond.sol`: + - Added DuplicateSignature error definition + +### Removed Files +- Deleted `test/DNSFacet.ts` +- Removed multipass sources: + - `src/facets/DNSFacet.sol` + - `src/initializers/MultipassInit.sol` + - `src/libraries/LibMultipass.sol` + - `src/interfaces/IMultipass.sol` + +### Mocks +- `RankifyInstanceEventMock.sol`: + - Fixed typo in parameter name from 'proposerIndicies' to 'proposerIndices' + +## Breaking Changes +- Storage layout changes in LibTBG require careful migration +- Payment handling completely reworked: + - Removed refunds functionality + - Implemented burn mechanism for 90% of payments + - Added DAO benefit for 10% of payments +- Rank token rewards simplified to only top player +- Solidity version updates may require dependency updates +- Renamed Diamond contract file requires build script updates +- Removed all multipass functionality + +## Migration Guide +1. Update build scripts to reference new DiamondClonable filename +2. Verify storage layout compatibility after LibTBG changes +3. Update dependencies to support Solidity 0.8.28 +4. Remove any references to multipass functionality +5. Update payment handling code to work with new burn mechanism +6. Adjust rank token distribution logic for single winner +7. Ensure game time parameters meet new constraints diff --git a/.gitignore b/.gitignore index 3a269d7f..81083520 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /node_modules /.pnp .pnp.js +.vscode # testing /coverage diff --git a/README.md b/README.md index 875504ef..8db81b17 100644 --- a/README.md +++ b/README.md @@ -66,24 +66,19 @@ const distributorArguments: MAODistribution.DistributorArgumentsStruct = { tokenName: 'tokenName', tokenSymbol: 'tokenSymbol', }, - ACIDSettings: { + RankifySettings: { RankTokenContractURI: 'https://example.com/rank', - gamePrice: 1, - joinGamePrice: 1, - maxPlayersSize: 16, - maxTurns: 1, + principalCost: RInstanceSettings.PRINCIPAL_COST, + principalTimeConstant: RInstanceSettings.PRINCIPAL_TIME_CONSTANT, metadata: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('metadata')), - minPlayersSize: 4, - paymentToken: rankify.address, rankTokenURI: 'https://example.com/rank', - timePerTurn: 1, - timeToJoin: 1, - voteCredits: 14, }, }; +// const abi = import('../abi/src/distributions/MAODistribution.sol/MAODistribution.json'); +// Encode the arguments const data = ethers.utils.defaultAbiCoder.encode( [ - 'tuple(tuple(string daoURI, string subdomain, bytes metadata, string tokenName, string tokenSymbol) DAOSEttings, tuple(uint256 timePerTurn, uint256 maxPlayersSize, uint256 minPlayersSize, uint256 timeToJoin, uint256 maxTurns, uint256 voteCredits, uint256 gamePrice, address paymentToken, uint256 joinGamePrice, string metadata, string rankTokenURI, string RankTokenContractURI) ACIDSettings)', + 'tuple(tuple(string daoURI, string subdomain, bytes metadata, string tokenName, string tokenSymbol) DAOSEttings, tuple(uint256 principalCost, uint256 principalTimeConstant, string metadata, string rankTokenURI, string RankTokenContractURI) RankifySettings)', ], [distributorArguments], ); @@ -93,19 +88,28 @@ const tx = await distributorContract.instantiate(distributorsDistId, data); In order to get `distributorsDistId` you can call `getDistributions` at `PeeramidLabsDistributor` contract and look for. We will host a public API to get the list of distributions soon. -### ACID distribution +### ACID Distribution (Autonomous Competence Identification Distribution) -[ArguableVotingTournament.sol](./src/distributions/ArguableVotingTournament.sol) is used to distribute governance tokens to the participants of the MAO by conducting autonomous competence identification tournaments. +[ArguableVotingTournament.sol](./src/distributions/ArguableVotingTournament.sol) implements a sophisticated tournament system for autonomous competence identification. It uses the Diamond pattern to provide a modular and upgradeable smart contract architecture. -This distribution deploys the Diamond Proxy that contains the following facets: +#### Core Components -- [EIP712InspectorFacet](./src/facets/EIP712InspectorFacet.sol) - Facet that contains the main logic of the distribution. -- [RankifyInstanceMainFacet](./src/facets//RankifyInstanceMainFacet.sol) - Facet that contains the main logic of the distribution. -- [RankifyGameMastersFacetFacet](./src/facets/RankifyInstanceGameMastersFacet.sol) - Facet that contains the main logic of the distribution. -- [RankifyInstanceGameOwnersFacet](./src/facets/RankifyInstanceGameOwnersFacet.sol) - Facet that contains the ownable logic of the distribution. (NB this will be deprecated) -- [RankifyInstanceRequirementsFacet](./src/facets/RankifyInstanceRequirementsFacet.sol) - Facet that contains the requirements logic of the distribution. +The distribution deploys a Diamond Proxy with the following facets: -To understand how it works further please refer to [docs.rankify.it](https://docs.rankify.it/governance) or ask us a question in [Discord](https://discord.gg/EddGgGUuWC) +- **EIP712InspectorFacet**: Handles message signing and verification using EIP-712 standard +- **RankifyInstanceMainFacet**: Core tournament logic including game creation, joining, and management +- **RankifyInstanceGameMastersFacet**: Manages voting and proposal submission mechanics +- **RankifyInstanceRequirementsFacet**: Handles participation requirements and constraints +- **DiamondLoupeFacet**: Standard Diamond pattern implementation for facet introspection +- **OwnershipFacet**: Manages contract ownership and permissions + +#### Key Features + +- Turn-based game mechanics with voting and proposal systems +- EIP-712 compliant message signing for secure interactions +- Modular architecture allowing for future upgrades +- Built-in reentrancy protection +- Integrated with the Rankify protocol for rank token management ## Contributing diff --git a/deploy/06_deoployMultipass.ts b/deploy/06_deoployMultipass.ts deleted file mode 100644 index c0c692d6..00000000 --- a/deploy/06_deoployMultipass.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { DeployFunction } from 'hardhat-deploy/types'; -import { MULTIPASS_CONTRACT_VERSION, MULTIPASS_CONTRACT_NAME } from '../test/utils'; -import { getProcessEnv } from '../scripts/libraries/utils'; -import { ethers } from 'hardhat'; -import { MultipassDiamond } from '../types'; -const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { - const { deployments, getNamedAccounts } = hre; - const { diamond } = deployments; - const { deployer, owner } = await getNamedAccounts(); - - const deployment = await diamond.deploy('Multipass', { - from: deployer, - owner: deployer, - log: true, - facets: ['DNSFacet', 'EIP712InspectorFacet', 'MultipassInit'], - execute: { - methodName: 'init', - args: - process.env.NODE_ENV === 'TEST' - ? [MULTIPASS_CONTRACT_NAME, MULTIPASS_CONTRACT_VERSION] - : [getProcessEnv(false, 'MULTIPASS_CONTRACT_NAME'), getProcessEnv(false, 'MULTIPASS_CONTRACT_VERSION')], - }, - }); - const multipass = (await ethers.getContractAt(deployment.abi, deployment.address)) as MultipassDiamond; - await multipass.connect(await hre.ethers.getSigner(deployer)).transferOwnership(owner); -}; - -export default func; -func.tags = ['multipass']; diff --git a/deploy/mao.ts b/deploy/mao.ts index d526da38..1edbd9c0 100644 --- a/deploy/mao.ts +++ b/deploy/mao.ts @@ -6,6 +6,7 @@ import { activeContractsList } from '@aragon/osx-ethers'; import { CodeIndex } from '@peeramid-labs/eds/types'; import CodeIndexAbi from '@peeramid-labs/eds/abi/src/CodeIndex.sol/CodeIndex.json'; import { MintSettingsStruct } from '../types/src/tokens/DistributableGovernanceERC20.sol/DistributableGovernanceERC20'; +import { ArguableVotingTournament } from '../types/src/distributions/ArguableVotingTournament'; const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const { deployments, getNamedAccounts } = hre; const { deploy } = deployments; @@ -17,7 +18,7 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { console.log('network', network, process.env.NODE_ENV); } if (!network) throw new Error('Network not provided'); - const { deployer } = await getNamedAccounts(); + const { deployer, DAO } = await getNamedAccounts(); const codeIndexContract = (await ethers.getContractAt( CodeIndexAbi, '0xc0D31d398c5ee86C5f8a23FA253ee8a586dA03Ce', @@ -101,34 +102,26 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { skipIfAlreadyDeployed: true, }); - const RankifyOwnerFacetDeployment = await deploy('RankifyInstanceGameOwnersFacet', { - from: deployer, - skipIfAlreadyDeployed: true, - }); - const OwnershipFacetDeployment = await deploy('OwnershipFacet', { from: deployer, skipIfAlreadyDeployed: true, }); + const addresses: ArguableVotingTournament.ArguableTournamentAddressesStruct = { + loupeFacet: loupeFacetDeployment.address, + inspectorFacet: inspectorFacetDeployment.address, + RankifyMainFacet: RankifyMainFacetDeployment.address, + RankifyReqsFacet: RankifyReqsFacetDeployment.address, + RankifyGMFacet: RankifyGMFacetDeployment.address, + OwnershipFacet: OwnershipFacetDeployment.address, + }; + const arguableVotingTournamentDeployment = await deploy('ArguableVotingTournament', { from: deployer, gasLimit: 8000000, estimatedGasLimit: 8000000, skipIfAlreadyDeployed: true, - args: [ - initializerAdr, - initializerSelector, - distributionName, - version, - loupeFacetDeployment.address, - inspectorFacetDeployment.address, - RankifyMainFacetDeployment.address, - RankifyReqsFacetDeployment.address, - RankifyGMFacetDeployment.address, - RankifyOwnerFacetDeployment.address, - OwnershipFacetDeployment.address, - ], + args: [initializerAdr, initializerSelector, distributionName, version, addresses], }); const arguableVotingTournamentDeploymentCode = await hre.ethers.provider.getCode( arguableVotingTournamentDeployment.address, @@ -160,7 +153,7 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { } const govTokenCode = await hre.ethers.provider.getCode(govTokenDeployment.address); const govTokenCodeId = ethers.utils.keccak256(govTokenCode); - + const rankifyToken = await deployments.get('Rankify'); const result = await deploy('MAODistribution', { from: deployer, skipIfAlreadyDeployed: true, @@ -168,6 +161,8 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { _tokenVotingPluginRepo, _daoFactory, _trustedForwarder, + rankifyToken.address, + DAO, rankTokenCodeId, arguableVotingTournamentCodeId, accessManagerId, @@ -183,8 +178,8 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { if (MaoDistrCodeIdAddress === ethers.constants.AddressZero) { await codeIndexContract.register(result.address); } - const code = await hre.ethers.provider.getCode(result.address); - const codeId = ethers.utils.keccak256(code); + // const code = await hre.ethers.provider.getCode(result.address); + // const codeId = ethers.utils.keccak256(code); // console.log('MAO deployed at', result.address, 'codeId', codeId); return; }; diff --git a/deploy/sacm.ts b/deploy/sacm.ts index 74f530a2..79ea152f 100644 --- a/deploy/sacm.ts +++ b/deploy/sacm.ts @@ -23,7 +23,9 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const codeId = ethers.utils.keccak256(code); const registerAddress = await codeIndexContract.get(codeId); if (registerAddress == ethers.constants.AddressZero) { - console.warn('registering contract', registerAddress, sacmDeployment.address, codeId); + if (process.env.NODE_ENV !== 'TEST') { + console.warn('registering contract', registerAddress, sacmDeployment.address, codeId); + } await codeIndexContract.register(sacmDeployment.address); } }; diff --git a/hardhat.config.ts b/hardhat.config.ts index 184d243b..2a180735 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -12,8 +12,9 @@ import 'hardhat-contract-sizer'; import 'hardhat-deploy'; import 'hardhat-tracer'; import 'solidity-docgen'; -import './playbook/initializeDomain'; import './playbook/createGame'; +import getSuperInterface from './scripts/getSuperInterface'; +import { ErrorFragment, EventFragment, FunctionFragment } from '@ethersproject/abi'; task('accounts', 'Prints the list of accounts', async (taskArgs, hre) => { const accounts = await hre.ethers.getSigners(); @@ -23,19 +24,28 @@ task('accounts', 'Prints the list of accounts', async (taskArgs, hre) => { } }); -// task("upload2IPFS", "Uploads files to ipfs") -// .addParam("path", "file path") -// .setAction(async (taskArgs) => { -// const data = fs.readFileSync(taskArgs.path); -// await ipfsUtils.upload2IPFS(data); -// }); - -// task("uploadDir2IPFS", "Uploads directory to ipfs") -// .addParam("path", "path") -// .setAction(async (taskArgs) => { -// await ipfsUtils.uploadDir2IPFS(taskArgs.path); -// }); - +task('getSuperInterface', 'Prints the super interface of a contract').setAction(async (taskArgs, hre) => { + const su = getSuperInterface(); + let return_value: { + functions: Record; + events: Record; + errors: Record; + } = { + functions: {}, + events: {}, + errors: {}, + }; + Object.values(su.functions).forEach(x => { + return_value['functions'][su.getSighash(x)] = x; + }); + Object.values(su.events).forEach(x => { + return_value['events'][su.getSighash(x)] = x; + }); + Object.values(su.errors).forEach(x => { + return_value['errors'][su.getSighash(x)] = x; + }); + console.log(JSON.stringify(return_value, null, 2)); +}); task('replaceFacet', 'Upgrades facet') .addParam('facet', 'facet') .addParam('address', 'contract address') @@ -99,6 +109,9 @@ export default { defaultPlayer: { localhost: '0xF52E5dF676f51E410c456CC34360cA6F27959420', }, + DAO: { + default: '0x520E00225C4a43B6c55474Db44a4a44199b4c3eE', + }, }, mocha: { timeout: 400000, @@ -148,6 +161,15 @@ export default { }, solidity: { compilers: [ + { + version: '0.8.28', + settings: { + optimizer: { + enabled: true, + runs: 2000, + }, + }, + }, { version: '0.8.20', settings: { @@ -169,19 +191,6 @@ export default { ], }, diamondAbi: [ - { - // (required) The name of your Diamond ABI - name: 'MultipassDiamond', - include: ['DNSFacet', 'OwnershipFacet', 'DiamondLoupeFacet', 'EIP712InspectorFacet'], - // We explicitly set `strict` to `true` because we want to validate our facets don't accidentally provide overlapping functions - strict: true, - // We use our diamond utils to filter some functions we ignore from the combined ABI - filter(abiElement: unknown, index: number, abi: unknown[], fullyQualifiedName: string) { - // const changes = new diamondUtils.DiamondChanges(); - const signature = toSignature(abiElement); - return isIncluded(fullyQualifiedName, signature); - }, - }, { name: 'RankifyDiamondInstance', include: [ @@ -191,9 +200,8 @@ export default { 'RankifyInstanceMainFacet', 'RankifyInstanceRequirementsFacet', 'RankifyInstanceGameMastersFacet', - 'RankifyInstanceGameOwnersFacet', ], - strict: true, + strict: false, filter(abiElement: unknown, index: number, abi: unknown[], fullyQualifiedName: string) { const signature = toSignature(abiElement); return isIncluded(fullyQualifiedName, signature); diff --git a/package.json b/package.json index 9959f45b..e13f87dd 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,8 @@ "@nomiclabs/buidler-web3": "^1.3.4", "@nomiclabs/hardhat-ethers": "^2.0.6", "@nomiclabs/hardhat-etherscan": "^3.1.8", - "@peeramid-labs/eds": "^2.1.0", + "@peeramid-labs/eds": "^2.1.3", "@shopify/eslint-plugin": "^43.0.0", - "@solidstate/contracts": "^0.0.35", "@typechain/ethers-v5": "^11.1.2", "@typechain/hardhat": "^7.0.0", "@typechain/web3-v1": "^6.0.1", @@ -49,20 +48,20 @@ "chai": "^4.3.6", "crypto-js": "^4.1.1", "eslint": "^9.6.0", - "prettier": "^3.3.3", "eslint-config-prettier": "^9.1.0", "eslint-plugin-promise": "^6.4.0", "eth-create2-calculator": "^1.1.5", "ethers": "^5.6.6", "globals": "^15.9.0", - "hardhat": "2.22.11", + "hardhat": "^2.22.16", "hardhat-abi-exporter": "^2.9.0", - "hardhat-contract-sizer": "^2.6.1", + "hardhat-contract-sizer": "^2.10.0", "hardhat-deploy": "^0.12.2", "hardhat-diamond-abi": "^3.0.0", "hardhat-tracer": "^3.1.0", "keccak": "^3.0.1", "mocha": "^10.0.0", + "prettier": "^3.3.3", "prettier-plugin-solidity": "^1.1.3", "solhint": "^3.6.2", "solidity-coverage": "^0.8.5", diff --git a/playbook/initializeDomain.ts b/playbook/initializeDomain.ts deleted file mode 100644 index 4637c237..00000000 --- a/playbook/initializeDomain.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { task, types } from 'hardhat/config'; -import { MultipassDiamond } from '../types'; -import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { LibMultipass } from '../types/src/facets/DNSFacet'; -import crypto from "crypto"; -import { signRegistrarMessage } from "../playbook/utils/utils"; - -task('domainInitialized', 'Initialize domain name and activate it') - .addOptionalParam('registrarAddress', 'Registrar address') - .addOptionalParam('domain', 'Domain name to register', 'Rankify.it') - .addOptionalParam('freeRegistrationsNumber', 'Free registration count number', '1000') - .addOptionalParam('fee', 'Fee amount in base currency of network', '0') - .addOptionalParam('reward', 'Referral share in base currency of network', '0') - .addOptionalParam('discount', 'Discount in base currency of network', '0') - .addOptionalParam('activate', 'Discount in base currency of network', true, types.boolean) - .addOptionalParam('username', 'Username to associate with account', '') - .addOptionalParam('player', 'Player address whose username will be set') - .setAction( - async ( - { - domain, - freeRegistrationsNumber, - fee, - reward, - discount, - registrarAddress, - activate, - username, - player, - }: { domain: string; freeRegistrationsNumber: string; fee: string; reward: string; discount: string; registrarAddress: string; activate: boolean; username: string; player: string; }, - hre: HardhatRuntimeEnvironment, - ) => { - const { deployments, getNamedAccounts } = hre; - const { owner, registrar, defaultPlayer } = await getNamedAccounts(); - const multipassDeployment = await deployments.get('Multipass'); - registrarAddress = registrarAddress ?? registrar; - const multipassContract = new hre.ethers.Contract( - multipassDeployment.address, - multipassDeployment.abi, - hre.ethers.provider.getSigner(owner), - ) as MultipassDiamond; - const tx = await multipassContract.initializeDomain( - registrarAddress, - freeRegistrationsNumber, - hre.ethers.utils.parseEther(fee), - hre.ethers.utils.formatBytes32String(domain), - hre.ethers.utils.parseEther(reward), - hre.ethers.utils.parseEther(discount), - ); - - if (activate === true) { - const tx = await multipassContract - .activateDomain(hre.ethers.utils.formatBytes32String(domain)); - console.log('Domain name "' + domain + '" successfully initialized and activated!'); - } - - if (username) { - player = player ?? defaultPlayer; - const playerId = crypto.randomUUID().slice(0, 31); - const registrarMessage = { - name: hre.ethers.utils.formatBytes32String(username), - id: hre.ethers.utils.formatBytes32String(playerId), - domainName: hre.ethers.utils.formatBytes32String(domain), - deadline: hre.ethers.BigNumber.from(9999), - nonce: hre.ethers.BigNumber.from(0), - }; - let signer = await hre.ethers.getSigner(registrar); - - const validSignature = await signRegistrarMessage(registrarMessage, multipassDeployment.address, signer, hre); - - let applicantData: LibMultipass.RecordStruct = { - name: hre.ethers.utils.formatBytes32String(username), - id: hre.ethers.utils.formatBytes32String(playerId), - wallet: player, - nonce: 0, - domainName: hre.ethers.utils.formatBytes32String(domain), - }; - - const emptyUserQuery: LibMultipass.NameQueryStruct = { - name: hre.ethers.utils.formatBytes32String(''), - id: hre.ethers.utils.formatBytes32String(''), - domainName: hre.ethers.utils.formatBytes32String(''), - wallet: hre.ethers.constants.AddressZero, - targetDomain: hre.ethers.utils.formatBytes32String(''), - }; - - const tx = await multipassContract.register( - applicantData, - registrarMessage.domainName, - validSignature, - registrarMessage.deadline, - emptyUserQuery, - hre.ethers.constants.HashZero, - ); - } - }, - ); -export default {}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bdb57373..5f006c3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@aragon/osx': specifier: ^1.3.0 - version: 1.3.0(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + version: 1.3.0(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@aragon/osx-ethers': specifier: ^1.3.0 version: 1.3.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -49,7 +49,7 @@ importers: version: 0.3.11 hardhat-gas-reporter: specifier: ^1.0.9 - version: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + version: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@17.0.45)(typescript@5.6.2) @@ -65,13 +65,13 @@ importers: version: 2.27.8 '@nomicfoundation/hardhat-chai-matchers': specifier: ^1.0.1 - version: 1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) '@nomicfoundation/hardhat-network-helpers': specifier: ^1.0.9 - version: 1.0.12(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 1.0.12(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) '@nomicfoundation/hardhat-toolbox': specifier: ^2.0.2 - version: 2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)))(@types/chai@4.3.20)(@types/mocha@9.1.1)(@types/node@17.0.45)(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(solidity-coverage@0.8.13(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2) + version: 2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)))(@types/chai@4.3.20)(@types/mocha@9.1.1)(@types/node@17.0.45)(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(solidity-coverage@0.8.13(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2) '@nomiclabs/buidler': specifier: ^1.4.8 version: 1.4.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -80,25 +80,22 @@ importers: version: 1.3.4(@nomiclabs/buidler@1.4.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@nomiclabs/hardhat-ethers': specifier: ^2.0.6 - version: 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) '@nomiclabs/hardhat-etherscan': specifier: ^3.1.8 - version: 3.1.8(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 3.1.8(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) '@peeramid-labs/eds': - specifier: ^2.1.0 - version: 2.1.0(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + specifier: ^2.1.3 + version: 2.1.3(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) '@shopify/eslint-plugin': specifier: ^43.0.0 version: 43.0.0(@babel/core@7.25.2)(eslint@9.11.1)(prettier@3.3.3)(typescript@5.6.2) - '@solidstate/contracts': - specifier: ^0.0.35 - version: 0.0.35 '@typechain/ethers-v5': specifier: ^11.1.2 version: 11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2) '@typechain/hardhat': specifier: ^7.0.0 - version: 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)) + version: 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)) '@typechain/web3-v1': specifier: ^6.0.1 version: 6.0.7(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2)(web3-core@1.10.4)(web3-eth-contract@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) @@ -139,23 +136,23 @@ importers: specifier: ^15.9.0 version: 15.9.0 hardhat: - specifier: 2.22.11 - version: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + specifier: ^2.22.16 + version: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) hardhat-abi-exporter: specifier: ^2.9.0 - version: 2.10.1(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 2.10.1(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) hardhat-contract-sizer: - specifier: ^2.6.1 - version: 2.10.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + specifier: ^2.10.0 + version: 2.10.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) hardhat-deploy: specifier: ^0.12.2 version: 0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) hardhat-diamond-abi: specifier: ^3.0.0 - version: 3.0.1(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + version: 3.0.1(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) hardhat-tracer: specifier: ^3.1.0 - version: 3.1.0(bufferutil@4.0.8)(chai@4.5.0)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + version: 3.1.0(bufferutil@4.0.8)(chai@4.5.0)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) keccak: specifier: ^3.0.1 version: 3.0.4 @@ -173,10 +170,10 @@ importers: version: 3.6.2(typescript@5.6.2) solidity-coverage: specifier: ^0.8.5 - version: 0.8.13(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 0.8.13(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) solidity-docgen: specifier: ^0.6.0-beta.36 - version: 0.6.0-beta.36(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 0.6.0-beta.36(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) typechain: specifier: ^8.0.0 version: 8.3.2(typescript@5.6.2) @@ -625,36 +622,36 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nomicfoundation/edr-darwin-arm64@0.5.2': - resolution: {integrity: sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==} + '@nomicfoundation/edr-darwin-arm64@0.6.5': + resolution: {integrity: sha512-A9zCCbbNxBpLgjS1kEJSpqxIvGGAX4cYbpDYCU2f3jVqOwaZ/NU761y1SvuCRVpOwhoCXqByN9b7HPpHi0L4hw==} engines: {node: '>= 18'} - '@nomicfoundation/edr-darwin-x64@0.5.2': - resolution: {integrity: sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==} + '@nomicfoundation/edr-darwin-x64@0.6.5': + resolution: {integrity: sha512-x3zBY/v3R0modR5CzlL6qMfFMdgwd6oHrWpTkuuXnPFOX8SU31qq87/230f4szM+ukGK8Hi+mNq7Ro2VF4Fj+w==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-arm64-gnu@0.5.2': - resolution: {integrity: sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==} + '@nomicfoundation/edr-linux-arm64-gnu@0.6.5': + resolution: {integrity: sha512-HGpB8f1h8ogqPHTyUpyPRKZxUk2lu061g97dOQ/W4CxevI0s/qiw5DB3U3smLvSnBHKOzYS1jkxlMeGN01ky7A==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-arm64-musl@0.5.2': - resolution: {integrity: sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==} + '@nomicfoundation/edr-linux-arm64-musl@0.6.5': + resolution: {integrity: sha512-ESvJM5Y9XC03fZg9KaQg3Hl+mbx7dsSkTIAndoJS7X2SyakpL9KZpOSYrDk135o8s9P9lYJdPOyiq+Sh+XoCbQ==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-x64-gnu@0.5.2': - resolution: {integrity: sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==} + '@nomicfoundation/edr-linux-x64-gnu@0.6.5': + resolution: {integrity: sha512-HCM1usyAR1Ew6RYf5AkMYGvHBy64cPA5NMbaeY72r0mpKaH3txiMyydcHibByOGdQ8iFLWpyUdpl1egotw+Tgg==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-x64-musl@0.5.2': - resolution: {integrity: sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==} + '@nomicfoundation/edr-linux-x64-musl@0.6.5': + resolution: {integrity: sha512-nB2uFRyczhAvWUH7NjCsIO6rHnQrof3xcCe6Mpmnzfl2PYcGyxN7iO4ZMmRcQS7R1Y670VH6+8ZBiRn8k43m7A==} engines: {node: '>= 18'} - '@nomicfoundation/edr-win32-x64-msvc@0.5.2': - resolution: {integrity: sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==} + '@nomicfoundation/edr-win32-x64-msvc@0.6.5': + resolution: {integrity: sha512-B9QD/4DSSCFtWicO8A3BrsnitO1FPv7axB62wq5Q+qeJ50yJlTmyeGY3cw62gWItdvy2mh3fRM6L1LpnHiB77A==} engines: {node: '>= 18'} - '@nomicfoundation/edr@0.5.2': - resolution: {integrity: sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==} + '@nomicfoundation/edr@0.6.5': + resolution: {integrity: sha512-tAqMslLP+/2b2sZP4qe9AuGxG3OkQ5gGgHE4isUuq6dUVjwCRPFhAOhpdFl+OjY5P3yEv3hmq9HjUGRa2VNjng==} engines: {node: '>= 18'} '@nomicfoundation/ethereumjs-common@4.0.4': @@ -819,8 +816,8 @@ packages: '@openzeppelin/contracts@5.0.2': resolution: {integrity: sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==} - '@peeramid-labs/eds@2.1.0': - resolution: {integrity: sha512-ICYwkCR7zXtN3SGEtgf/OvcdYr6oSitksW4aM6Eb5XgQdS91bkcQ3rpRrFcEzN58zhC6WOPgOrDzMOu3hJ3YNA==} + '@peeramid-labs/eds@2.1.3': + resolution: {integrity: sha512-r0ftwP79CidH2NfbtkQ3X8ugwd8cPjpodltoI2FyUqmIyLOOBpf5YFhraeDeKjf5K3IdrxVPz1Mszz7iPYqhdA==} '@pkgr/core@0.1.1': resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} @@ -902,9 +899,6 @@ packages: '@solidity-parser/parser@0.5.2': resolution: {integrity: sha512-uRyvnvVYmgNmTBpWDbBsH/0kPESQhQpEc4KsvMRLVzFJ1o1s0uIv0Y6Y9IB5vI1Dwz2CbS4X/y4Wyw/75cTFnQ==} - '@solidstate/contracts@0.0.35': - resolution: {integrity: sha512-NwaXt+Pv8NS4CT1+n1vPCk3RWG4RiiNAvOQMrHF46T3c2qZLcaRkWKLMyQpXYgbPELuhG7ZMKT4OJ+W4Xb5lCw==} - '@szmarczak/http-timer@4.0.6': resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} @@ -2706,6 +2700,14 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2969,10 +2971,6 @@ packages: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} deprecated: Glob versions prior to v9 are no longer supported - glob@7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} - deprecated: Glob versions prior to v9 are no longer supported - glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -3100,8 +3098,8 @@ packages: chai: 4.x hardhat: '>=2.22.5 <3.x' - hardhat@2.22.11: - resolution: {integrity: sha512-g9xr6BGXbzj2sqG9AjHwqeUOS9v2NwLbuq7rsdjMB2RLWmYp8IFdZnzq8UewwLJisuWgiygB+dwLktjqAbRuOw==} + hardhat@2.22.16: + resolution: {integrity: sha512-d52yQZ09u0roL6GlgJSvtknsBtIuj9JrJ/U8VMzr/wue+gO5v2tQayvOX6llerlR57Zw2EOTQjLAt6RpHvjwHA==} hasBin: true peerDependencies: ts-node: '*' @@ -4348,6 +4346,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -5077,6 +5079,10 @@ packages: tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} + title-case@2.1.1: resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==} @@ -5797,9 +5803,9 @@ snapshots: - bufferutil - utf-8-validate - '@aragon/osx@1.3.0(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@aragon/osx@1.3.0(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@ensdomains/ens-contracts': 0.0.11(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@ensdomains/ens-contracts': 0.0.11(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@openzeppelin/contracts': 4.8.1 '@openzeppelin/contracts-upgradeable': 4.8.1 transitivePeerDependencies: @@ -6174,9 +6180,9 @@ snapshots: nano-base32: 1.0.1 ripemd160: 2.0.2 - '@ensdomains/buffer@0.0.13(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@ensdomains/buffer@0.0.13(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@nomiclabs/hardhat-truffle5': 2.0.7(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-truffle5': 2.0.7(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) transitivePeerDependencies: - '@nomiclabs/hardhat-web3' - bufferutil @@ -6190,9 +6196,9 @@ snapshots: - web3-eth-abi - web3-utils - '@ensdomains/ens-contracts@0.0.11(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@ensdomains/ens-contracts@0.0.11(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@ensdomains/buffer': 0.0.13(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@ensdomains/buffer': 0.0.13(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@ensdomains/solsha1': 0.0.3 '@openzeppelin/contracts': 4.8.1 dns-packet: 5.6.1 @@ -6641,29 +6647,29 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@nomicfoundation/edr-darwin-arm64@0.5.2': {} + '@nomicfoundation/edr-darwin-arm64@0.6.5': {} - '@nomicfoundation/edr-darwin-x64@0.5.2': {} + '@nomicfoundation/edr-darwin-x64@0.6.5': {} - '@nomicfoundation/edr-linux-arm64-gnu@0.5.2': {} + '@nomicfoundation/edr-linux-arm64-gnu@0.6.5': {} - '@nomicfoundation/edr-linux-arm64-musl@0.5.2': {} + '@nomicfoundation/edr-linux-arm64-musl@0.6.5': {} - '@nomicfoundation/edr-linux-x64-gnu@0.5.2': {} + '@nomicfoundation/edr-linux-x64-gnu@0.6.5': {} - '@nomicfoundation/edr-linux-x64-musl@0.5.2': {} + '@nomicfoundation/edr-linux-x64-musl@0.6.5': {} - '@nomicfoundation/edr-win32-x64-msvc@0.5.2': {} + '@nomicfoundation/edr-win32-x64-msvc@0.6.5': {} - '@nomicfoundation/edr@0.5.2': + '@nomicfoundation/edr@0.6.5': dependencies: - '@nomicfoundation/edr-darwin-arm64': 0.5.2 - '@nomicfoundation/edr-darwin-x64': 0.5.2 - '@nomicfoundation/edr-linux-arm64-gnu': 0.5.2 - '@nomicfoundation/edr-linux-arm64-musl': 0.5.2 - '@nomicfoundation/edr-linux-x64-gnu': 0.5.2 - '@nomicfoundation/edr-linux-x64-musl': 0.5.2 - '@nomicfoundation/edr-win32-x64-msvc': 0.5.2 + '@nomicfoundation/edr-darwin-arm64': 0.6.5 + '@nomicfoundation/edr-darwin-x64': 0.6.5 + '@nomicfoundation/edr-linux-arm64-gnu': 0.6.5 + '@nomicfoundation/edr-linux-arm64-musl': 0.6.5 + '@nomicfoundation/edr-linux-x64-gnu': 0.6.5 + '@nomicfoundation/edr-linux-x64-musl': 0.6.5 + '@nomicfoundation/edr-win32-x64-msvc': 0.6.5 '@nomicfoundation/ethereumjs-common@4.0.4': dependencies: @@ -6685,41 +6691,41 @@ snapshots: '@nomicfoundation/ethereumjs-rlp': 5.0.4 ethereum-cryptography: 0.1.3 - '@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': + '@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': dependencies: '@ethersproject/abi': 5.7.0 - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) '@types/chai-as-promised': 7.1.8 chai: 4.5.0 chai-as-promised: 7.1.2(chai@4.5.0) deep-eql: 4.1.4 ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) ordinal: 1.0.3 - '@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': + '@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': dependencies: ethereumjs-util: 7.1.5 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) - ? '@nomicfoundation/hardhat-toolbox@2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)))(@types/chai@4.3.20)(@types/mocha@9.1.1)(@types/node@17.0.45)(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(solidity-coverage@0.8.13(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2)' + ? '@nomicfoundation/hardhat-toolbox@2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)))(@types/chai@4.3.20)(@types/mocha@9.1.1)(@types/node@17.0.45)(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(solidity-coverage@0.8.13(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2)' : dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@nomicfoundation/hardhat-chai-matchers': 1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) - '@nomicfoundation/hardhat-network-helpers': 1.0.12(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-etherscan': 3.1.8(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-chai-matchers': 1.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-network-helpers': 1.0.12(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-etherscan': 3.1.8(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) '@typechain/ethers-v5': 11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2) - '@typechain/hardhat': 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)) + '@typechain/hardhat': 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2)) '@types/chai': 4.3.20 '@types/mocha': 9.1.1 '@types/node': 17.0.45 chai: 4.5.0 ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) - hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) - solidity-coverage: 0.8.13(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + solidity-coverage: 0.8.13(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) ts-node: 10.9.2(@types/node@17.0.45)(typescript@5.6.2) typechain: 8.3.2(typescript@5.6.2) typescript: 5.6.2 @@ -6830,12 +6836,12 @@ snapshots: safe-buffer: 5.2.1 util.promisify: 1.1.2 - '@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': + '@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': dependencies: ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) - '@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': + '@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/address': 5.7.0 @@ -6843,7 +6849,7 @@ snapshots: chalk: 2.4.2 debug: 4.3.7(supports-color@8.1.1) fs-extra: 7.0.1 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) lodash: 4.17.21 semver: 6.3.1 table: 6.8.2 @@ -6851,15 +6857,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@nomiclabs/hardhat-truffle5@2.0.7(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@nomiclabs/hardhat-truffle5@2.0.7(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@nomiclabs/hardhat-web3': 2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-web3': 2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@nomiclabs/truffle-contract': 4.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@types/chai': 4.3.20 chai: 4.5.0 ethereumjs-util: 7.1.5 fs-extra: 7.0.1 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) web3: 1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -6871,10 +6877,10 @@ snapshots: - web3-eth-abi - web3-utils - '@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@nomiclabs/hardhat-web3@2.0.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: '@types/bignumber.js': 5.0.0 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) web3: 1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@nomiclabs/truffle-contract@4.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': @@ -6914,7 +6920,7 @@ snapshots: '@openzeppelin/contracts@5.0.2': {} - '@peeramid-labs/eds@2.1.0(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': + '@peeramid-labs/eds@2.1.3(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abi': 5.7.0 '@openzeppelin/contracts': 5.0.2 @@ -6924,7 +6930,7 @@ snapshots: '@types/node': 17.0.45 chalk: 4.1.2 cli-table: 0.3.11 - hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) ts-node: 10.9.2(@types/node@17.0.45)(typescript@5.6.2) typescript: 5.6.2 transitivePeerDependencies: @@ -7070,8 +7076,6 @@ snapshots: '@solidity-parser/parser@0.5.2': {} - '@solidstate/contracts@0.0.35': {} - '@szmarczak/http-timer@4.0.6': dependencies: defer-to-connect: 2.0.1 @@ -7170,14 +7174,14 @@ snapshots: typechain: 8.3.2(typescript@5.6.2) typescript: 5.6.2 - '@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))': + '@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@typechain/ethers-v5@11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@typechain/ethers-v5': 11.1.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) fs-extra: 9.1.0 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) typechain: 8.3.2(typescript@5.6.2) '@typechain/web3-v1@6.0.7(typechain@8.3.2(typescript@5.6.2))(typescript@5.6.2)(web3-core@1.10.4)(web3-eth-contract@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': @@ -9428,6 +9432,10 @@ snapshots: dependencies: pend: 1.2.0 + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -9711,15 +9719,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - glob@7.2.0: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -9876,17 +9875,17 @@ snapshots: ajv: 6.12.6 har-schema: 2.0.0 - hardhat-abi-exporter@2.10.1(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): + hardhat-abi-exporter@2.10.1(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): dependencies: '@ethersproject/abi': 5.7.0 delete-empty: 3.0.0 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) - hardhat-contract-sizer@2.10.0(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): + hardhat-contract-sizer@2.10.0(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): dependencies: chalk: 4.1.2 cli-table3: 0.6.5 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) strip-ansi: 6.0.1 hardhat-deploy@0.12.4(bufferutil@4.0.8)(utf-8-validate@5.0.10): @@ -9920,21 +9919,21 @@ snapshots: - supports-color - utf-8-validate - hardhat-diamond-abi@3.0.1(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): + hardhat-diamond-abi@3.0.1(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): dependencies: debug: 4.3.7(supports-color@8.1.1) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): + hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): dependencies: array-uniq: 1.0.3 eth-gas-reporter: 0.2.27(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' @@ -9942,24 +9941,24 @@ snapshots: - debug - utf-8-validate - hardhat-tracer@3.1.0(bufferutil@4.0.8)(chai@4.5.0)(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): + hardhat-tracer@3.1.0(bufferutil@4.0.8)(chai@4.5.0)(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): dependencies: chai: 4.5.0 chalk: 4.1.2 debug: 4.3.7(supports-color@8.1.1) ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) semver: 7.6.3 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10): + hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 '@metamask/eth-sig-util': 4.0.1 - '@nomicfoundation/edr': 0.5.2 + '@nomicfoundation/edr': 0.6.5 '@nomicfoundation/ethereumjs-common': 4.0.4 '@nomicfoundation/ethereumjs-tx': 5.0.4 '@nomicfoundation/ethereumjs-util': 9.0.4 @@ -9971,7 +9970,6 @@ snapshots: aggregate-error: 3.1.0 ansi-escapes: 4.3.2 boxen: 5.1.2 - chalk: 2.4.2 chokidar: 4.0.1 ci-info: 2.0.0 debug: 4.3.7(supports-color@8.1.1) @@ -9979,10 +9977,9 @@ snapshots: env-paths: 2.2.1 ethereum-cryptography: 1.2.0 ethereumjs-abi: 0.6.8 - find-up: 2.1.0 + find-up: 5.0.0 fp-ts: 1.19.3 fs-extra: 7.0.1 - glob: 7.2.0 immutable: 4.3.7 io-ts: 1.10.4 json-stream-stringify: 3.1.4 @@ -9991,12 +9988,14 @@ snapshots: mnemonist: 0.38.5 mocha: 10.7.3 p-map: 4.0.0 + picocolors: 1.1.0 raw-body: 2.5.2 resolve: 1.17.0 semver: 6.3.1 solc: 0.8.26(debug@4.3.7) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 + tinyglobby: 0.2.10 tsort: 0.0.1 undici: 5.28.4 uuid: 8.3.2 @@ -11265,6 +11264,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -11824,7 +11825,7 @@ snapshots: solidity-ast@0.4.59: {} - solidity-coverage@0.8.13(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): + solidity-coverage@0.8.13(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): dependencies: '@ethersproject/abi': 5.7.0 '@solidity-parser/parser': 0.18.0 @@ -11835,7 +11836,7 @@ snapshots: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) jsonschema: 1.4.1 lodash: 4.17.21 mocha: 10.7.3 @@ -11847,10 +11848,10 @@ snapshots: shelljs: 0.8.5 web3-utils: 1.10.4 - solidity-docgen@0.6.0-beta.36(hardhat@2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): + solidity-docgen@0.6.0-beta.36(hardhat@2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): dependencies: handlebars: 4.7.8 - hardhat: 2.22.11(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat: 2.22.16(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@17.0.45)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) solidity-ast: 0.4.59 sort-keys-length@1.0.1: @@ -12162,6 +12163,11 @@ snapshots: tiny-case@1.0.3: {} + tinyglobby@0.2.10: + dependencies: + fdir: 6.4.2(picomatch@4.0.2) + picomatch: 4.0.2 + title-case@2.1.1: dependencies: no-case: 2.3.2 diff --git a/scripts/getSuperInterface.ts b/scripts/getSuperInterface.ts new file mode 100644 index 00000000..fa384f29 --- /dev/null +++ b/scripts/getSuperInterface.ts @@ -0,0 +1,35 @@ +import { JsonFragment } from '@ethersproject/abi'; +import fs from 'fs'; +import path from 'path'; +import { ethers } from 'ethers'; + +const getSuperInterface = (outputPath?: string) => { + let mergedArray: JsonFragment[] = []; + function readDirectory(directory: string) { + const files = fs.readdirSync(directory); + + files.forEach(file => { + const fullPath = path.join(directory, file); + if (fs.statSync(fullPath).isDirectory()) { + readDirectory(fullPath); // Recurse into subdirectories + } else if (path.extname(file) === '.json') { + const fileContents = require('../' + fullPath); // Load the JSON file + if (Array.isArray(fileContents)) { + mergedArray = mergedArray.concat(fileContents); // Merge the array from the JSON file + } + } + }); + } + const originalConsoleLog = console.log; + readDirectory('./abi'); + readDirectory('./node_modules/@peeramid-labs/eds/abi'); + console.log = () => {}; // avoid noisy output + const result = new ethers.utils.Interface(mergedArray); + if (outputPath) { + fs.writeFileSync(outputPath, JSON.stringify(result.fragments, null, 2)); + } + console.log = originalConsoleLog; + return result; +}; + +export default getSuperInterface; diff --git a/scripts/libraries/diamond.js b/scripts/libraries/diamond.js index d40b2f3b..3f58e394 100644 --- a/scripts/libraries/diamond.js +++ b/scripts/libraries/diamond.js @@ -150,7 +150,7 @@ async function deployDiamond(FacetNames, signer, initializer, initializerArgs) { console.log('DiamondCutFacet deployed:', diamondCutFacet.address); } - const Diamond = await ethers.getContractFactory('DiamondCloneable', signer); + const Diamond = await ethers.getContractFactory('DiamondClonable', signer); const diamond = await Diamond.deploy(signer.address, diamondCutFacet.address); await diamond.deployed(); if (require.main === module) { diff --git a/scripts/libraries/generateDistributorData.ts b/scripts/libraries/generateDistributorData.ts new file mode 100644 index 00000000..fd48160c --- /dev/null +++ b/scripts/libraries/generateDistributorData.ts @@ -0,0 +1,13 @@ +import { ethers } from 'ethers'; +import { MAODistribution } from '../../types/src/distributions/MAODistribution'; + +export function generateDistributorData(args: MAODistribution.DistributorArgumentsStruct): string { + const data = ethers.utils.defaultAbiCoder.encode( + [ + 'tuple(tuple(string daoURI, string subdomain, bytes metadata, string tokenName, string tokenSymbol) DAOSEttings, tuple(uint256 principalCost, uint256 principalTimeConstant, string metadata, string rankTokenURI, string RankTokenContractURI) RankifySettings)', + ], + [args], + ); + return data; +} +export default generateDistributorData; diff --git a/scripts/libraries/multipass.ts b/scripts/libraries/multipass.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/scripts/playbooks/instantiateMAO.ts b/scripts/playbooks/instantiateMAO.ts index 97ea01c0..f527e5fd 100644 --- a/scripts/playbooks/instantiateMAO.ts +++ b/scripts/playbooks/instantiateMAO.ts @@ -26,8 +26,8 @@ export const instantiateMAO = async ({ govToken: instances[1], govTokenAccessManager: instances[2], ACIDInstance: instances[3], - ACIDAccessManager: instances[12], - rankToken: instances[13], + ACIDAccessManager: instances[11], + rankToken: instances[12], instanceId: evts[0].args.newInstanceId, distributorsId: evts[0].args.distributionId, }; diff --git a/src/abstracts/LockableERC1155.sol b/src/abstracts/LockableERC1155.sol index c3b70018..e257e420 100644 --- a/src/abstracts/LockableERC1155.sol +++ b/src/abstracts/LockableERC1155.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol"; import {ILockableERC1155} from "../interfaces/ILockableERC1155.sol"; /** @@ -9,7 +9,7 @@ import {ILockableERC1155} from "../interfaces/ILockableERC1155.sol"; * @dev This is an abstract contract that extends the ERC1155 token contract and implements the ILockableERC1155 interface. * It provides functionality to lock and unlock token amounts for specific accounts and IDs. */ -abstract contract LockableERC1155 is ERC1155Upgradeable, ILockableERC1155 { +abstract contract LockableERC1155 is ERC1155BurnableUpgradeable, ILockableERC1155 { struct LockableERC1155Storage { mapping(address => mapping(uint256 tokenId => uint256)) lockedAmounts; } @@ -95,4 +95,18 @@ abstract contract LockableERC1155 is ERC1155Upgradeable, ILockableERC1155 { } super._update(from, to, ids, values); } + + function burn( + address account, + uint256 id, + uint256 value + ) public virtual override(ERC1155BurnableUpgradeable, ILockableERC1155) { + if (getLockableERC1155Storage().lockedAmounts[account][id] + value > balanceOf(account, id)) + revert insufficient( + id, + balanceOf(account, id), + getLockableERC1155Storage().lockedAmounts[account][id] + value + ); + super.burn(account, id, value); + } } diff --git a/src/distributions/ArguableVotingTournament.sol b/src/distributions/ArguableVotingTournament.sol index 066a8384..953378e0 100644 --- a/src/distributions/ArguableVotingTournament.sol +++ b/src/distributions/ArguableVotingTournament.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity =0.8.28; import "./InitializedDiamondDistribution.sol"; import "../vendor/diamond/facets/DiamondLoupeFacet.sol"; @@ -8,15 +8,17 @@ import "../vendor/diamond/facets/OwnershipFacet.sol"; import "../facets/RankifyInstanceMainFacet.sol"; import "../facets/RankifyInstanceRequirementsFacet.sol"; import "../facets/RankifyInstanceGameMastersFacet.sol"; -import "../facets/RankifyInstanceGameOwnersFacet.sol"; import "../initializers/RankifyInstanceInit.sol"; import "../vendor/diamond/interfaces/IDiamondCut.sol"; import "@peeramid-labs/eds/src/libraries/LibSemver.sol"; /** * @title ArguableVotingTournament Distribution - * @notice This contract implements a diamond distribution for Ethereum Distribution System. It is reponsible to create new instance of ArguableVotingTournament. - * @dev It is expected to be used ONLY by the Distributor contract. + * @notice This contract implements a diamond distribution for the Ethereum Distribution System (EDS). + * It creates and manages instances of ArguableVotingTournament, enabling decentralized + * tournament management with voting capabilities. + * @dev This contract follows the Diamond pattern and is designed to be used exclusively by the + * Distributor contract. It manages facets for tournament operations, voting, and game master functions. * @author Peeramid Labs, 2024 */ contract ArguableVotingTournament is InitializedDiamondDistribution { @@ -25,60 +27,71 @@ contract ArguableVotingTournament is InitializedDiamondDistribution { RankifyInstanceMainFacet private immutable _RankifyMainFacet; RankifyInstanceRequirementsFacet private immutable _RankifyReqsFacet; RankifyInstanceGameMastersFacet private immutable _RankifyGMFacet; - RankifyInstanceGameOwnersFacet private immutable _RankifyOwnerFacet; OwnershipFacet private immutable _OwnershipFacet; address private immutable _initializer; bytes32 private immutable distributionName; uint256 private immutable distributionVersion; + /** + * @dev Utility function to convert function signature strings to selectors + * @param signature The function signature as a string + * @return bytes4 The corresponding function selector + */ function stringToSelector(string memory signature) private pure returns (bytes4) { return bytes4(keccak256(bytes(signature))); } /** - * @dev Constructor for the ArguableVotingTournament contract. - * - * Note Initializer function will be added as a regular facet to the Diamond Proxy, - * Since it is expected that initialization is done by distributor contract, the initializer will not be run, hence - * it is up for distributor to remove this facet upon succesfull initialization. + * @dev Groups the addresses of all required facets for the tournament + * @notice This struct helps organize the deployment of the diamond proxy system + */ + struct ArguableTournamentAddresses { + address loupeFacet; + address inspectorFacet; + address RankifyMainFacet; + address RankifyReqsFacet; + address RankifyGMFacet; + address OwnershipFacet; + } + + /** + * @dev Constructor for the ArguableVotingTournament contract + * @notice Sets up the diamond proxy system with all required facets and initializes core components + * @dev The initializer function is added as a regular facet to the Diamond Proxy. + * Since initialization is handled by the distributor contract, it's expected that + * the distributor will remove this facet after successful initialization. */ constructor( address initializer, bytes4 initializerSelector, bytes32 _distributionName, LibSemver.Version memory version, - address loupeFacet, - address inspectorFacet, - address RankifyMainFacet, - address RankifyReqsFacet, - address RankifyGMFacet, - address RankifyOwnerFacet, - address OwnershipFacetAddr + ArguableTournamentAddresses memory addresses ) InitializedDiamondDistribution(address(this), bytes32(0), initializerSelector) { _initializer = initializer; - _loupeFacet = DiamondLoupeFacet(loupeFacet); - _inspectorFacet = EIP712InspectorFacet(inspectorFacet); - _RankifyMainFacet = RankifyInstanceMainFacet(RankifyMainFacet); - _RankifyReqsFacet = RankifyInstanceRequirementsFacet(RankifyReqsFacet); - _RankifyGMFacet = RankifyInstanceGameMastersFacet(RankifyGMFacet); - _RankifyOwnerFacet = RankifyInstanceGameOwnersFacet(RankifyOwnerFacet); - _OwnershipFacet = OwnershipFacet(OwnershipFacetAddr); + _loupeFacet = DiamondLoupeFacet(addresses.loupeFacet); + _inspectorFacet = EIP712InspectorFacet(addresses.inspectorFacet); + _RankifyMainFacet = RankifyInstanceMainFacet(addresses.RankifyMainFacet); + _RankifyReqsFacet = RankifyInstanceRequirementsFacet(addresses.RankifyReqsFacet); + _RankifyGMFacet = RankifyInstanceGameMastersFacet(addresses.RankifyGMFacet); + _OwnershipFacet = OwnershipFacet(addresses.OwnershipFacet); distributionName = _distributionName; distributionVersion = LibSemver.toUint256(version); } /** - * @dev see Ethereum Distribution System IDistribute for interface specification. - * @return instances Array[9]: [diamond proxy, 8x diamond facets..] + * @notice see Ethereum Distribution System IDistribute for interface specification. + * @return instances Array[8]: [diamond proxy, 8x diamond facets..] * @return distributionName: bytes32 encoded name to be used in EIP712 signing flow * @return distributionVersion: uint256 encoded distribution version. Can be parsed to eip712 signature with EDS LibSemver + * @dev // instances: 0 - diamond; 1 - DiamondLoupeFacet; 2 - EIP712InspectorFacet; 3 - RankifyInstanceMainFacet; 4 - RankifyInstanceRequirementsFacet; 5 - RankifyInstanceGameMastersFacet // 6 - OwnershipFacet */ function instantiate(bytes memory) external override returns (address[] memory instances, bytes32, uint256) { (address[] memory _instances, , ) = super._instantiate(); address diamond = _instances[0]; - IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](8); + IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](7); bytes4[] memory loupeSelectors = new bytes4[](4); loupeSelectors[0] = DiamondLoupeFacet.facets.selector; @@ -100,41 +113,41 @@ contract ArguableVotingTournament is InitializedDiamondDistribution { action: IDiamondCut.FacetCutAction.Add, functionSelectors: EIP712InspectorFacetSelectors }); - bytes4[] memory RankifyInstanceMainFacetSelectors = new bytes4[](28); + bytes4[] memory RankifyInstanceMainFacetSelectors = new bytes4[](27); RankifyInstanceMainFacetSelectors[0] = RankifyInstanceMainFacet.cancelGame.selector; RankifyInstanceMainFacetSelectors[1] = RankifyInstanceMainFacet.gameCreator.selector; - RankifyInstanceMainFacetSelectors[2] = stringToSelector("createGame(address,uint256,uint256)"); - RankifyInstanceMainFacetSelectors[3] = stringToSelector("createGame(address,uint256,uint256,address[])"); - RankifyInstanceMainFacetSelectors[4] = stringToSelector("createGame(address,uint256)"); - RankifyInstanceMainFacetSelectors[5] = RankifyInstanceMainFacet.leaveGame.selector; - RankifyInstanceMainFacetSelectors[6] = RankifyInstanceMainFacet.joinGame.selector; - RankifyInstanceMainFacetSelectors[7] = RankifyInstanceMainFacet.openRegistration.selector; - RankifyInstanceMainFacetSelectors[8] = RankifyInstanceMainFacet.startGame.selector; - RankifyInstanceMainFacetSelectors[9] = RankifyInstanceMainFacet.onERC1155BatchReceived.selector; - RankifyInstanceMainFacetSelectors[10] = RankifyInstanceMainFacet.onERC1155Received.selector; - RankifyInstanceMainFacetSelectors[11] = RankifyInstanceMainFacet.onERC721Received.selector; - RankifyInstanceMainFacetSelectors[12] = RankifyInstanceMainFacet.getContractState.selector; - RankifyInstanceMainFacetSelectors[13] = RankifyInstanceMainFacet.getTurn.selector; - RankifyInstanceMainFacetSelectors[14] = RankifyInstanceMainFacet.getGM.selector; - RankifyInstanceMainFacetSelectors[15] = RankifyInstanceMainFacet.getScores.selector; - RankifyInstanceMainFacetSelectors[16] = RankifyInstanceMainFacet.isOvertime.selector; - RankifyInstanceMainFacetSelectors[17] = RankifyInstanceMainFacet.isGameOver.selector; - RankifyInstanceMainFacetSelectors[18] = RankifyInstanceMainFacet.getPlayersGame.selector; - RankifyInstanceMainFacetSelectors[19] = RankifyInstanceMainFacet.isLastTurn.selector; - RankifyInstanceMainFacetSelectors[20] = RankifyInstanceMainFacet.isRegistrationOpen.selector; - RankifyInstanceMainFacetSelectors[21] = RankifyInstanceMainFacet.getGameRank.selector; - RankifyInstanceMainFacetSelectors[22] = RankifyInstanceMainFacet.getPlayers.selector; - RankifyInstanceMainFacetSelectors[23] = RankifyInstanceMainFacet.canStartGame.selector; - RankifyInstanceMainFacetSelectors[24] = RankifyInstanceMainFacet.canEndTurn.selector; - RankifyInstanceMainFacetSelectors[25] = RankifyInstanceMainFacet.isPlayerTurnComplete.selector; - RankifyInstanceMainFacetSelectors[26] = RankifyInstanceMainFacet.getPlayerVotedArray.selector; - RankifyInstanceMainFacetSelectors[27] = RankifyInstanceMainFacet.getPlayersMoved.selector; + RankifyInstanceMainFacetSelectors[2] = RankifyInstanceMainFacet.createGame.selector; + RankifyInstanceMainFacetSelectors[3] = RankifyInstanceMainFacet.leaveGame.selector; + RankifyInstanceMainFacetSelectors[4] = RankifyInstanceMainFacet.joinGame.selector; + RankifyInstanceMainFacetSelectors[5] = RankifyInstanceMainFacet.openRegistration.selector; + RankifyInstanceMainFacetSelectors[6] = RankifyInstanceMainFacet.startGame.selector; + RankifyInstanceMainFacetSelectors[7] = RankifyInstanceMainFacet.onERC1155BatchReceived.selector; + RankifyInstanceMainFacetSelectors[8] = RankifyInstanceMainFacet.onERC1155Received.selector; + RankifyInstanceMainFacetSelectors[9] = RankifyInstanceMainFacet.onERC721Received.selector; + RankifyInstanceMainFacetSelectors[10] = RankifyInstanceMainFacet.getContractState.selector; + RankifyInstanceMainFacetSelectors[11] = RankifyInstanceMainFacet.getTurn.selector; + RankifyInstanceMainFacetSelectors[12] = RankifyInstanceMainFacet.getGM.selector; + RankifyInstanceMainFacetSelectors[13] = RankifyInstanceMainFacet.getScores.selector; + RankifyInstanceMainFacetSelectors[14] = RankifyInstanceMainFacet.isOvertime.selector; + RankifyInstanceMainFacetSelectors[15] = RankifyInstanceMainFacet.isGameOver.selector; + RankifyInstanceMainFacetSelectors[16] = RankifyInstanceMainFacet.getPlayersGame.selector; + RankifyInstanceMainFacetSelectors[17] = RankifyInstanceMainFacet.isLastTurn.selector; + RankifyInstanceMainFacetSelectors[18] = RankifyInstanceMainFacet.isRegistrationOpen.selector; + RankifyInstanceMainFacetSelectors[19] = RankifyInstanceMainFacet.getGameRank.selector; + RankifyInstanceMainFacetSelectors[20] = RankifyInstanceMainFacet.getPlayers.selector; + RankifyInstanceMainFacetSelectors[21] = RankifyInstanceMainFacet.canStartGame.selector; + RankifyInstanceMainFacetSelectors[22] = RankifyInstanceMainFacet.canEndTurn.selector; + RankifyInstanceMainFacetSelectors[23] = RankifyInstanceMainFacet.isPlayerTurnComplete.selector; + RankifyInstanceMainFacetSelectors[24] = RankifyInstanceMainFacet.getPlayerVotedArray.selector; + RankifyInstanceMainFacetSelectors[25] = RankifyInstanceMainFacet.getPlayersMoved.selector; + RankifyInstanceMainFacetSelectors[26] = RankifyInstanceMainFacet.estimateGamePrice.selector; facetCuts[2] = IDiamondCut.FacetCut({ facetAddress: address(_RankifyMainFacet), action: IDiamondCut.FacetCutAction.Add, functionSelectors: RankifyInstanceMainFacetSelectors }); + bytes4[] memory RankifyInstanceRequirementsFacetSelectors = new bytes4[](3); RankifyInstanceRequirementsFacetSelectors[0] = RankifyInstanceRequirementsFacet.setJoinRequirements.selector; RankifyInstanceRequirementsFacetSelectors[1] = RankifyInstanceRequirementsFacet.getJoinRequirements.selector; @@ -159,41 +172,25 @@ contract ArguableVotingTournament is InitializedDiamondDistribution { functionSelectors: RankifyInstanceGameMastersFacetSelectors }); - bytes4[] memory RankifyInstanceGameOwnersFacetSelectors = new bytes4[](8); - - RankifyInstanceGameOwnersFacetSelectors[0] = RankifyInstanceGameOwnersFacet.setGamePrice.selector; - RankifyInstanceGameOwnersFacetSelectors[1] = RankifyInstanceGameOwnersFacet.setJoinGamePrice.selector; - RankifyInstanceGameOwnersFacetSelectors[2] = RankifyInstanceGameOwnersFacet.setRankTokenAddress.selector; - RankifyInstanceGameOwnersFacetSelectors[3] = RankifyInstanceGameOwnersFacet.setTimePerTurn.selector; - RankifyInstanceGameOwnersFacetSelectors[4] = RankifyInstanceGameOwnersFacet.setMaxPlayersSize.selector; - RankifyInstanceGameOwnersFacetSelectors[5] = RankifyInstanceGameOwnersFacet.setMinPlayersSize.selector; - RankifyInstanceGameOwnersFacetSelectors[6] = RankifyInstanceGameOwnersFacet.setTimeToJoin.selector; - RankifyInstanceGameOwnersFacetSelectors[7] = RankifyInstanceGameOwnersFacet.setMaxTurns.selector; - facetCuts[5] = IDiamondCut.FacetCut({ - facetAddress: address(_RankifyOwnerFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: RankifyInstanceGameOwnersFacetSelectors - }); - bytes4[] memory OwnershipFacetSelectors = new bytes4[](2); OwnershipFacetSelectors[0] = _OwnershipFacet.transferOwnership.selector; OwnershipFacetSelectors[1] = _OwnershipFacet.owner.selector; - facetCuts[6] = IDiamondCut.FacetCut({ + facetCuts[5] = IDiamondCut.FacetCut({ facetAddress: address(_OwnershipFacet), action: IDiamondCut.FacetCutAction.Add, functionSelectors: OwnershipFacetSelectors }); bytes4[] memory initializerSelectors = new bytes4[](1); initializerSelectors[0] = RankifyInstanceInit.init.selector; - facetCuts[7] = IDiamondCut.FacetCut({ + facetCuts[6] = IDiamondCut.FacetCut({ facetAddress: _initializer, action: IDiamondCut.FacetCutAction.Add, functionSelectors: initializerSelectors }); super.initialize(DiamondCutFacet(diamond), facetCuts, ""); - address[] memory returnValue = new address[](9); + address[] memory returnValue = new address[](8); returnValue[0] = diamond; returnValue[1] = facetCuts[0].facetAddress; returnValue[2] = facetCuts[1].facetAddress; @@ -202,7 +199,8 @@ contract ArguableVotingTournament is InitializedDiamondDistribution { returnValue[5] = facetCuts[4].facetAddress; returnValue[6] = facetCuts[5].facetAddress; returnValue[7] = facetCuts[6].facetAddress; - returnValue[8] = facetCuts[7].facetAddress; + //renouncing ownership + OwnershipFacet(diamond).transferOwnership(address(0)); return (returnValue, distributionName, distributionVersion); } diff --git a/src/distributions/DiamondDistribution.sol b/src/distributions/DiamondDistribution.sol index ce44f752..d89b95dd 100644 --- a/src/distributions/DiamondDistribution.sol +++ b/src/distributions/DiamondDistribution.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity =0.8.28; import "@peeramid-labs/eds/src/abstracts/CloneDistribution.sol"; -import "../vendor/diamond/DiamondCloneable.sol"; +import "../vendor/diamond/DiamondClonable.sol"; import "../vendor/diamond/facets/DiamondCutFacet.sol"; /** @@ -17,7 +17,7 @@ contract DiamondDistribution is CloneDistribution { constructor(address owner) { address diamondCutFacet = address(new DiamondCutFacet()); // Deploy the diamond proxy contract - address diamondProxy = address(new DiamondCloneable(owner, diamondCutFacet)); + address diamondProxy = address(new DiamondClonable(owner, diamondCutFacet)); _reference = diamondProxy; } diff --git a/src/distributions/InitializedDiamondDistribution.sol b/src/distributions/InitializedDiamondDistribution.sol index 86ee41f5..15c772c2 100644 --- a/src/distributions/InitializedDiamondDistribution.sol +++ b/src/distributions/InitializedDiamondDistribution.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "../vendor/diamond/DiamondCloneable.sol"; +import "../vendor/diamond/DiamondClonable.sol"; import "../vendor/diamond/facets/DiamondCutFacet.sol"; -import "../distributions/DiamondDistribution.sol"; +import "./DiamondDistribution.sol"; import "../vendor/diamond/libraries/LibDiamond.sol"; import "../vendor/diamond/interfaces/IDiamondCut.sol"; diff --git a/src/distributions/MAODistribution.sol b/src/distributions/MAODistribution.sol index ca3e503c..9e6f6380 100644 --- a/src/distributions/MAODistribution.sol +++ b/src/distributions/MAODistribution.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity =0.8.28; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import "@peeramid-labs/eds/src/interfaces/IDistribution.sol"; @@ -12,27 +12,20 @@ import {RankToken} from "../tokens/RankToken.sol"; import "../initializers/RankifyInstanceInit.sol"; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "@peeramid-labs/eds/src/abstracts/CodeIndexer.sol"; - +import "hardhat/console.sol"; import {TokenSettings, VotingMode, VotingSettings, IPluginRepo, IDAOFactory} from "../vendor/aragon/interfaces.sol"; /** * @title MAODistribution * @dev This contract implements the IDistribution and CodeIndexer interfaces. It uses the Clones library for address cloning. * - * @notice The contract is responsible for creating and managing DAOs and ACID distributions. + * @notice The contract is responsible for creating and managing DAOs and Rankify distributions. * @author Peeramid Labs, 2024 */ contract MAODistribution is IDistribution, CodeIndexer { - struct UserACIDSettings { - uint256 timePerTurn; - uint256 maxPlayersSize; - uint256 minPlayersSize; - uint256 timeToJoin; - uint256 maxTurns; - uint256 voteCredits; - uint256 gamePrice; - address paymentToken; - uint256 joinGamePrice; + struct UserRankifySettings { + uint256 principalCost; + uint96 principalTimeConstant; string metadata; string rankTokenURI; string RankTokenContractURI; @@ -48,7 +41,7 @@ contract MAODistribution is IDistribution, CodeIndexer { struct DistributorArguments { OSxDistributionArguments DAOSEttings; - UserACIDSettings ACIDSettings; + UserRankifySettings RankifySettings; } using Clones for address; @@ -58,9 +51,11 @@ contract MAODistribution is IDistribution, CodeIndexer { bytes32 private immutable _distributionName; uint256 private immutable _distributionVersion; address private immutable _rankTokenBase; - IDistribution private immutable _ACIDDistributionBase; + IDistribution private immutable _RankifyDistributionBase; address private immutable _governanceERC20Base; address private immutable _accessManagerBase; + address private immutable _paymentToken; + address private immutable _beneficiary; /** * @notice Initializes the contract with the provided parameters and performs necessary checks. @@ -69,13 +64,13 @@ contract MAODistribution is IDistribution, CodeIndexer { * - EIP712 compatible name/version can be extracted with use of LibSemver * * - * WARNING: _trustedForwarder functionality hasn't been yet reviewed nor implemented for ACID distribution and if set will affect only OSx DAO setup. + * WARNING: _trustedForwarder functionality hasn't been yet reviewed nor implemented for Rankify distribution and if set will affect only OSx DAO setup. * * @param tokenVotingPluginRepo Address of the token voting plugin repository. * @param daoFactory Address of the Aragons DAO factory. * @param trustedForwarder Address of the trusted forwarder. * @param rankTokenCodeId Identifier for the rank token code. - * @param ACIDDIistributionId Identifier for the ACID distribution. + * @param RankifyDIistributionId Identifier for the Rankify distribution. * @param accessManagerId Identifier for the access manager. * @param governanceERC20BaseId Identifier for the governance ERC20 base. * @param distributionName Name of the distribution. @@ -85,8 +80,10 @@ contract MAODistribution is IDistribution, CodeIndexer { address tokenVotingPluginRepo, address daoFactory, address trustedForwarder, + address paymentToken, + address beneficiary, bytes32 rankTokenCodeId, - bytes32 ACIDDIistributionId, + bytes32 RankifyDIistributionId, bytes32 accessManagerId, bytes32 governanceERC20BaseId, bytes32 distributionName, @@ -99,12 +96,21 @@ contract MAODistribution is IDistribution, CodeIndexer { _distributionName = distributionName; _distributionVersion = LibSemver.toUint256(distributionVersion); _rankTokenBase = getContractsIndex().get(rankTokenCodeId); + + if (beneficiary == address(0)) { + revert("Beneficiary not found"); + } + _beneficiary = beneficiary; + if (paymentToken == address(0)) { + revert("Payment token not found"); + } + _paymentToken = paymentToken; if (_rankTokenBase == address(0)) { revert("Rank token base not found"); } - _ACIDDistributionBase = IDistribution(getContractsIndex().get(ACIDDIistributionId)); - if (address(_ACIDDistributionBase) == address(0)) { - revert("ACID distribution base not found"); + _RankifyDistributionBase = IDistribution(getContractsIndex().get(RankifyDIistributionId)); + if (address(_RankifyDistributionBase) == address(0)) { + revert("Rankify distribution base not found"); } _accessManagerBase = getContractsIndex().get(accessManagerId); @@ -155,8 +161,8 @@ contract MAODistribution is IDistribution, CodeIndexer { SimpleAccessManager.SimpleAccessManagerInitializer[] memory govTokenAccessSettings = new SimpleAccessManager.SimpleAccessManagerInitializer[](1); govTokenAccessSettings[0].selector = DistributableGovernanceERC20.mint.selector; - govTokenAccessSettings[0].dissallowedAddresses = new address[](1); - govTokenAccessSettings[0].dissallowedAddresses[0] = createdDao; + govTokenAccessSettings[0].disallowedAddresses = new address[](1); + govTokenAccessSettings[0].disallowedAddresses[0] = createdDao; govTokenAccessSettings[0].distributionComponentsOnly = true; SimpleAccessManager govTokenAccessManager = SimpleAccessManager(_accessManagerBase.clone()); @@ -178,8 +184,8 @@ contract MAODistribution is IDistribution, CodeIndexer { return (returnValue, "OSxDistribution", 1); } - function createACID( - UserACIDSettings memory args, + function createRankify( + UserRankifySettings memory args, address dao ) internal returns (address[] memory instances, bytes32, uint256) { address rankToken = _rankTokenBase.clone(); @@ -191,28 +197,29 @@ contract MAODistribution is IDistribution, CodeIndexer { rankTokenSelectors[3] = RankToken.batchMint.selector; rankTokenSelectors[4] = RankToken.setURI.selector; rankTokenSelectors[5] = RankToken.setContractURI.selector; + SimpleAccessManager rankTokenAccessManager = SimpleAccessManager(_accessManagerBase.clone()); SimpleAccessManager.SimpleAccessManagerInitializer[] memory RankTokenAccessSettings = new SimpleAccessManager.SimpleAccessManagerInitializer[](6); RankTokenAccessSettings[0].selector = RankToken.mint.selector; - RankTokenAccessSettings[0].dissallowedAddresses = new address[](1); - RankTokenAccessSettings[0].dissallowedAddresses[0] = dao; + RankTokenAccessSettings[0].disallowedAddresses = new address[](1); + RankTokenAccessSettings[0].disallowedAddresses[0] = dao; RankTokenAccessSettings[0].distributionComponentsOnly = true; RankTokenAccessSettings[1].selector = RankToken.lock.selector; - RankTokenAccessSettings[1].dissallowedAddresses = new address[](1); - RankTokenAccessSettings[1].dissallowedAddresses[0] = dao; + RankTokenAccessSettings[1].disallowedAddresses = new address[](1); + RankTokenAccessSettings[1].disallowedAddresses[0] = dao; RankTokenAccessSettings[1].distributionComponentsOnly = true; RankTokenAccessSettings[2].selector = RankToken.unlock.selector; - RankTokenAccessSettings[2].dissallowedAddresses = new address[](1); - RankTokenAccessSettings[2].dissallowedAddresses[0] = dao; + RankTokenAccessSettings[2].disallowedAddresses = new address[](1); + RankTokenAccessSettings[2].disallowedAddresses[0] = dao; RankTokenAccessSettings[2].distributionComponentsOnly = true; RankTokenAccessSettings[3].selector = RankToken.batchMint.selector; - RankTokenAccessSettings[3].dissallowedAddresses = new address[](1); - RankTokenAccessSettings[3].dissallowedAddresses[0] = dao; + RankTokenAccessSettings[3].disallowedAddresses = new address[](1); + RankTokenAccessSettings[3].disallowedAddresses[0] = dao; RankTokenAccessSettings[3].distributionComponentsOnly = true; RankTokenAccessSettings[4].selector = RankToken.setURI.selector; @@ -221,44 +228,36 @@ contract MAODistribution is IDistribution, CodeIndexer { RankTokenAccessSettings[5].selector = RankToken.setContractURI.selector; RankTokenAccessSettings[5].distributionComponentsOnly = true; - SimpleAccessManager rankTokenAccessManager = SimpleAccessManager(_accessManagerBase.clone()); - rankTokenAccessManager.initialize(RankTokenAccessSettings, rankToken, IDistributor(msg.sender)); // msg.sender must be IDistributor or it will revert - RankToken(rankToken).initialize(args.rankTokenURI, args.RankTokenContractURI, address(rankTokenAccessManager)); + ( - address[] memory ACIDDistrAddresses, - bytes32 ACIDDistributionname, - uint256 ACIDDistributionVersion - ) = _ACIDDistributionBase.instantiate(abi.encode(dao, rankToken, args.metadata)); - - RankifyInstanceInit.contractInitializer memory ACIDInit = RankifyInstanceInit.contractInitializer({ - timePerTurn: args.timePerTurn, - maxPlayersSize: args.maxPlayersSize, - minPlayersSize: args.minPlayersSize, + address[] memory RankifyDistrAddresses, + bytes32 RankifyDistributionName, + uint256 RankifyDistributionVersion + ) = _RankifyDistributionBase.instantiate(abi.encode(dao, rankToken, args.metadata)); + + RankifyInstanceInit.contractInitializer memory RankifyInit = RankifyInstanceInit.contractInitializer({ rewardToken: rankToken, - timeToJoin: args.timeToJoin, - gamePrice: args.gamePrice, - joinGamePrice: args.joinGamePrice, - maxTurns: args.maxTurns, - numWinners: 1, - voteCredits: args.voteCredits, - paymentToken: args.paymentToken + principalCost: args.principalCost, + principalTimeConstant: args.principalTimeConstant, + paymentToken: _paymentToken, + beneficiary: _beneficiary }); - RankifyInstanceInit(ACIDDistrAddresses[0]).init( - string(abi.encodePacked(ACIDDistributionname)), - LibSemver.toString(LibSemver.parse(ACIDDistributionVersion)), - ACIDInit + RankifyInstanceInit(RankifyDistrAddresses[0]).init( + string(abi.encodePacked(RankifyDistributionName)), + LibSemver.toString(LibSemver.parse(RankifyDistributionVersion)), + RankifyInit ); - address[] memory returnValue = new address[](ACIDDistrAddresses.length + 2); - for (uint256 i; i < ACIDDistrAddresses.length; ++i) { - returnValue[i] = ACIDDistrAddresses[i]; + address[] memory returnValue = new address[](RankifyDistrAddresses.length + 2); + for (uint256 i; i < RankifyDistrAddresses.length; ++i) { + returnValue[i] = RankifyDistrAddresses[i]; } - returnValue[ACIDDistrAddresses.length] = address(rankTokenAccessManager); - returnValue[ACIDDistrAddresses.length + 1] = rankToken; + returnValue[RankifyDistrAddresses.length] = address(rankTokenAccessManager); + returnValue[RankifyDistrAddresses.length + 1] = rankToken; - return (returnValue, ACIDDistributionname, ACIDDistributionVersion); + return (returnValue, RankifyDistributionName, RankifyDistributionVersion); } /** @@ -267,7 +266,7 @@ contract MAODistribution is IDistribution, CodeIndexer { * @return instances An array of addresses representing the new instances. * @return distributionName A bytes32 value representing the name of the distribution. * @return distributionVersion A uint256 value representing the version of the distribution. - * @dev `instances` array contents: DAO, GovernanceToken, Gov Token AccessManager, ACID Diamond, 8x ACID Diamond facets, RankTokenAccessManager, RankToken + * @dev `instances` array contents: DAO, GovernanceToken, Gov Token AccessManager, Rankify Diamond, 8x Rankify Diamond facets, RankTokenAccessManager, RankToken */ function instantiate( bytes memory data @@ -275,15 +274,15 @@ contract MAODistribution is IDistribution, CodeIndexer { DistributorArguments memory args = abi.decode(data, (DistributorArguments)); (address[] memory DAOInstances, , ) = createOSxDAO(args.DAOSEttings); - (address[] memory ACIDInstances, , ) = createACID(args.ACIDSettings, DAOInstances[0]); + (address[] memory RankifyInstances, , ) = createRankify(args.RankifySettings, DAOInstances[0]); - address[] memory returnValue = new address[](DAOInstances.length + ACIDInstances.length); + address[] memory returnValue = new address[](DAOInstances.length + RankifyInstances.length); for (uint256 i; i < DAOInstances.length; ++i) { returnValue[i] = DAOInstances[i]; } - for (uint256 i; i < ACIDInstances.length; ++i) { - returnValue[DAOInstances.length + i] = ACIDInstances[i]; + for (uint256 i; i < RankifyInstances.length; ++i) { + returnValue[DAOInstances.length + i] = RankifyInstances[i]; } return (returnValue, _distributionName, _distributionVersion); } @@ -298,7 +297,7 @@ contract MAODistribution is IDistribution, CodeIndexer { srcs[1] = address(_daoFactory); srcs[2] = address(_trustedForwarder); srcs[3] = address(_rankTokenBase); - srcs[4] = address(_ACIDDistributionBase); + srcs[4] = address(_RankifyDistributionBase); srcs[6] = address(_governanceERC20Base); srcs[7] = address(_accessManagerBase); return (srcs, _distributionName, _distributionVersion); @@ -313,7 +312,7 @@ contract MAODistribution is IDistribution, CodeIndexer { return DistributorArguments({ DAOSEttings: OSxDistributionArguments("", "", "", "", ""), - ACIDSettings: UserACIDSettings(0, 0, 0, 0, 0, 0, 0, address(0), 0, "", "", "") + RankifySettings: UserRankifySettings(0, 0, "", "", "") }); } } diff --git a/src/distributors/DAODistributor.sol b/src/distributors/DAODistributor.sol index de12e330..da09272d 100644 --- a/src/distributors/DAODistributor.sol +++ b/src/distributors/DAODistributor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity =0.8.20; import "@peeramid-labs/eds/src/abstracts/TokenizedDistributor.sol"; import "@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol"; diff --git a/src/facets/DNSFacet.sol b/src/facets/DNSFacet.sol deleted file mode 100644 index 9d1ca46a..00000000 --- a/src/facets/DNSFacet.sol +++ /dev/null @@ -1,298 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; -// import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "../abstracts/draft-EIP712Diamond.sol"; -import "../interfaces/IMultipass.sol"; -import "../libraries/LibMultipass.sol"; -import "../modifiers/OnlyOwnerDiamond.sol"; - -// Consider upgrade for https://eips.ethereum.org/EIPS/eip-4834 - -contract DNSFacet is EIP712, IMultipass { - using ECDSA for bytes32; - using LibMultipass for bytes32; - - // using LibMultipass for LibMultipass.Record; - using LibMultipass for LibMultipass.Record; - using LibMultipass for bytes; - - function _isValidSignature( - bytes memory message, - bytes memory signature, - address account - ) internal view returns (bool) { - bytes32 typedHash = _hashTypedDataV4(keccak256(message)); - return SignatureChecker.isValidSignatureNow(account, typedHash, signature); - } - - function _validateRegistration( - LibMultipass.Record memory newRecord, - bytes32 domainName, - bytes memory registrarSignature, - uint256 signatureDeadline - ) private view { - LibMultipass.NameQuery memory query = LibMultipass.queryFromRecord(newRecord, domainName); - //Check name query is legit - require(LibMultipass._checkNotEmpty(query.id), "_validateNameQuery-> new record id cannot be empty"); - require( - LibMultipass._checkNotEmpty(query.domainName), - "_validateNameQuery-> new record domain cannot be empty" - ); - require(query.wallet != address(0), "_validateNameQuery-> new ecord address cannot be empty"); - - //Check query does not resolves (name already exists) - (bool nameExists, ) = LibMultipass.resolveRecord(query); - require(nameExists == false, "User already registered, use modify instead"); - //Check LibMultipass.Domain is legit - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(query.domainName); - require(_domain.properties.isActive, "Multipass->register: domain is not active"); - - //check signatures and time - require(signatureDeadline > block.number, "Multipass->register: Deadline is less than current block number"); - - { - bytes memory registrarMessage = abi.encode( - LibMultipass._TYPEHASH, - query.name, - query.id, - query.domainName, - signatureDeadline, - 0 - ); - - require( - _isValidSignature(registrarMessage, registrarSignature, _domain.properties.registrar), - "Multipass->register: Registrar signature is not valid" - ); - } - { - (bool status, ) = LibMultipass.resolveRecord(query); - require(status == false, "Multipass->register: applicant is already registered, use modify instread"); - } - } - - function initializeDomain( - address registrar, - uint256 freeRegistrationsNumber, - uint256 fee, - bytes32 domainName, - uint256 referrerReward, - uint256 referralDiscount - ) public override onlyOwner { - require(registrar != address(0), "Multipass->initializeDomain: You must provide a registrar address"); - require(LibMultipass._checkNotEmpty(domainName), "Multipass->initializeDomain: Domain name cannot be empty"); - require( - LibMultipass.resolveDomainIndex(domainName) == 0, - "Multipass->initializeDomain: Domain name already exists" - ); - (bool status, uint256 result) = Math.tryAdd(referrerReward, referralDiscount); - require(status == true, "Multipass->initializeDomain: referrerReward + referralDiscount overflow"); - require(result <= fee, "Multipass->initializeDomain: referral values are higher then fee itself"); - - LibMultipass._initializeDomain( - registrar, - freeRegistrationsNumber, - fee, - domainName, - referrerReward, - referralDiscount - ); - emit InitializedDomain(registrar, freeRegistrationsNumber, fee, domainName, referrerReward, referralDiscount); - } - - function _enforseDomainNameIsValid(bytes32 domainName) private view { - require(domainName._checkNotEmpty(), "activateDomain->Please specify LibMultipass.Domain name"); - require(domainName.resolveDomainIndex() != 0, "Domain does not exist"); - } - - function activateDomain(bytes32 domainName) public override onlyOwner { - _enforseDomainNameIsValid(domainName); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - _domain.properties.isActive = true; - emit DomainActivated(domainName); - } - - function deactivateDomain(bytes32 domainName) public override onlyOwner { - _enforseDomainNameIsValid(domainName); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - _domain.properties.isActive = false; - } - - function changeFee(bytes32 domainName, uint256 fee) public override onlyOwner { - _enforseDomainNameIsValid(domainName); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - uint256 _referrerReward = _domain.properties.referrerReward; - uint256 _referralDiscount = _domain.properties.referralDiscount; - require( - _referralDiscount + _referrerReward <= fee, - "Multipass->changeFee: referral rewards would become too high" - ); - _domain.properties.fee = fee; - emit DomainFeeChanged(domainName, fee); - } - - function changeRegistrar(bytes32 domainName, address newRegistrar) public override onlyOwner { - _enforseDomainNameIsValid(domainName); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - require(newRegistrar != address(0), "new registrar cannot be zero"); - _domain.properties.registrar = newRegistrar; - } - - function deleteName( - LibMultipass.NameQuery memory query // bytes32 domainName, // address wallet, // bytes32 username, // bytes32 id - ) public override onlyOwner { - _enforseDomainNameIsValid(query.domainName); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(query.domainName); - query.targetDomain = ""; - (bool status, LibMultipass.Record memory r) = resolveRecord(query); - require(status == true, "Multipass->deleteName: name not resolved"); - _domain.addressToId[r.wallet] = bytes32(0); - _domain.idToAddress[r.id] = address(0); - _domain.idToName[r.id] = bytes32(0); - _domain.nameToId[r.name] = bytes32(0); - _domain.nonce[r.id] += 1; - _domain.properties.registerSize--; - - emit nameDeleted(_domain.properties.name, r.wallet, r.id, r.name); - } - - function changeReferralProgram( - uint256 referrerReward, - uint256 freeRegistrations, - uint256 referralDiscount, - bytes32 domainName - ) public override onlyOwner { - _enforseDomainNameIsValid(domainName); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - (bool status, uint256 result) = Math.tryAdd(referrerReward, referralDiscount); - require(status == true, "Multipass->changeReferralProgram: referrerReward + referralDiscount overflow"); - require( - result <= _domain.properties.fee, - "Multipass->changeReferralProgram: referral values are higher then the fee itself" - ); - _domain.properties.referrerReward = referrerReward; - _domain.properties.referralDiscount = referralDiscount; - _domain.properties.freeRegistrationsNumber = freeRegistrations; - emit ReferralProgramChanged(domainName, referrerReward, referralDiscount, freeRegistrations); - } - - /** - @dev resolves LibMultipass.Record of name query in to status and identity */ - function resolveRecord( - LibMultipass.NameQuery memory query - ) public view override returns (bool, LibMultipass.Record memory) { - return LibMultipass.resolveRecord(query); - } - - function register( - LibMultipass.Record memory newRecord, - bytes32 domainName, - bytes memory registrarSignature, - uint256 signatureDeadline, - LibMultipass.NameQuery memory referrer, - bytes memory referralCode - ) public payable override { - _enforseDomainNameIsValid(domainName); - _validateRegistration(newRecord, domainName, registrarSignature, signatureDeadline); - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - (bool hasValidReferrer, LibMultipass.Record memory referrerRecord) = LibMultipass.resolveRecord(referrer); - uint256 referrersShare = 0; - if (!LibMultipass.shouldRegisterForFree(_domain)) { - referrersShare = hasValidReferrer ? _domain.properties.referrerReward : 0; - uint256 valueToPay = _domain.properties.fee - (hasValidReferrer ? _domain.properties.referralDiscount : 0); - require(msg.value >= valueToPay, "Multipass->register: Payment value is not enough"); - } - LibMultipass._registerNew(newRecord, _domain); - emit Registered(_domain.properties.name, newRecord); - if (hasValidReferrer) { - bytes memory refferalMessage = abi.encode(LibMultipass._TYPEHASH_REFERRAL, referrerRecord.wallet); - require( - _isValidSignature(refferalMessage, referralCode, referrerRecord.wallet), - "Multipass->register: Referral code is not valid" - ); - require( - payable(referrerRecord.wallet).send(referrersShare), - "Multipass->register: Failed to send referral reward" - ); - require(referrerRecord.wallet != newRecord.wallet, "Cannot refer yourself"); - emit Referred(referrerRecord, newRecord, domainName); - } - } - - function getModifyPrice(LibMultipass.NameQuery memory query) public view override returns (uint256) { - (bool userExists, LibMultipass.Record memory record) = LibMultipass.resolveRecord(query); - require(userExists == true, "getModifyPrice->user not found "); - return LibMultipass._getModifyPrice(record); - } - - function modifyUserName( - bytes32 domainName, - LibMultipass.NameQuery memory query, - bytes32 newName, - bytes memory registrarSignature, - uint256 signatureDeadline - ) public payable override { - _enforseDomainNameIsValid(domainName); - query.targetDomain = domainName; - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - require(_domain.properties.isActive, "Multipass->modifyUserName: LibMultipass.Domain is not active"); - require(newName != bytes32(0), "Multipass->modifyUserName: Name cannot be empty"); - require( - signatureDeadline >= block.number, - "Multipass->modifyUserName: Signature deadline must be greater than current block number" - ); - - (bool userExists, LibMultipass.Record memory userRecord) = LibMultipass.resolveRecord(query); - LibMultipass.Record memory newRecord = userRecord; - bytes32 oldName = newRecord.name; - newRecord.name = newName; - require(userExists == true, "user does not exist, use register() instead"); - bytes memory registrarMessage = abi.encode( - LibMultipass._TYPEHASH, - newRecord.name, - newRecord.id, - newRecord.domainName, - signatureDeadline, - userRecord.nonce - ); - require( - _isValidSignature(registrarMessage, registrarSignature, _domain.properties.registrar), - "Multipass->modifyUserName: Not a valid signature" - ); - - uint256 _fee = LibMultipass._getModifyPrice(newRecord); - - require(msg.value >= _fee, "Multipass->modifyUserName: Not enough payment"); - require(_domain.nonce[userRecord.id] == userRecord.nonce, "Multipass->modifyUserName: invalid nonce"); - require(_domain.nameToId[newName] == bytes32(0), "OveMultipass->modifyUserName: new name already exists"); - - LibMultipass._setRecord(_domain, newRecord); - _domain.nameToId[_domain.idToName[newRecord.id]] = bytes32(0); - - emit UserRecordModified(newRecord, oldName, domainName); - } - - function getBalance() external view override returns (uint256) { - return address(this).balance; - } - - function getDomainState(bytes32 domainName) external view override returns (LibMultipass.Domain memory) { - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); - return _domain.properties; - } - - function getDomainStateByIdx(uint256 index) external view returns (LibMultipass.Domain memory) { - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorageByIdx(index); - return _domain.properties; - } - - function getContractState() external view override returns (uint256) { - return LibMultipass._getContractState(); - } - - function withrawFunds(address to) public override onlyOwner { - payable(to).transfer(address(this).balance); - } -} diff --git a/src/facets/RankifyInstanceGameMastersFacet.sol b/src/facets/RankifyInstanceGameMastersFacet.sol index 6abbab7b..7599d9e2 100644 --- a/src/facets/RankifyInstanceGameMastersFacet.sol +++ b/src/facets/RankifyInstanceGameMastersFacet.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity =0.8.28; import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; import {LibRankify} from "../libraries/LibRankify.sol"; -import {IRankifyInstanceCommons} from "../interfaces/IRankifyInstanceCommons.sol"; +import {IRankifyInstance} from "../interfaces/IRankifyInstance.sol"; import "../abstracts/DiamondReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; @@ -16,9 +16,8 @@ import "../vendor/diamond/libraries/LibDiamond.sol"; contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { using LibTBG for uint256; using LibRankify for uint256; - using LibTBG for LibTBG.GameInstance; + using LibTBG for LibTBG.State; event OverTime(uint256 indexed gameId); - event LastTurn(uint256 indexed gameId); event ProposalScore( uint256 indexed gameId, uint256 indexed turn, @@ -32,12 +31,11 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { address[] players, uint256[] scores, string[] newProposals, - uint256[] proposerIndicies, + uint256[] proposerIndices, uint256[][] votes ); - + event LastTurn(uint256 indexed gameId); event GameOver(uint256 indexed gameId, address[] players, uint256[] scores); - event ProposalSubmitted( uint256 indexed gameId, uint256 indexed turn, @@ -62,7 +60,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { * - Releases the coins for the game with `gameId`, the game creator, the top player, and `player`. */ function onPlayersGameEnd(uint256 gameId, address player) private { - IRankifyInstanceCommons.RInstance storage game = gameId.getGameStorage(); + LibRankify.GameState storage game = gameId.getGameState(); LibCoinVending.release(bytes32(gameId), game.createdBy, gameId.getLeaderBoard()[0], player); } @@ -87,7 +85,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { require(!gameId.isGameOver(), "Game over"); gameId.enforceIsPlayingGame(voter); require(gameId.getTurn() > 1, "No proposals exist at turn 1: cannot vote"); - IRankifyInstanceCommons.RInstance storage game = gameId.getGameStorage(); + LibRankify.GameState storage game = gameId.getGameState(); require(!game.playerVoted[voter], "Already voted"); game.numVotesThisTurn += 1; game.playerVoted[voter] = true; @@ -109,7 +107,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { require(!proposalData.gameId.isGameOver(), "Game over"); proposalData.gameId.enforceHasStarted(); - IRankifyInstanceCommons.RInstance storage game = proposalData.gameId.getGameStorage(); + LibRankify.GameState storage game = proposalData.gameId.getGameState(); require(LibTBG.getPlayersGame(proposalData.proposer) == proposalData.gameId, "not a player"); // require(!proposalData.gameId.isLastTurn(), "Cannot propose in last turn"); require(bytes(proposalData.encryptedProposal).length != 0, "Cannot propose empty"); @@ -136,7 +134,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { * - Increments the number of ongoing proposals of the game with `gameId` by the number of `newProposals`. */ function _afterNextTurn(uint256 gameId, string[] memory newProposals) private { - IRankifyInstanceCommons.RInstance storage game = gameId.getGameStorage(); + LibRankify.GameState storage game = gameId.getGameState(); for (uint256 i = 0; i < newProposals.length; ++i) { game.ongoingProposals[i] = newProposals[i]; } @@ -162,7 +160,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { emit LastTurn(gameId); } if (_isGameOver) { - uint256[] memory finalScores = gameId.closeGame(LibDiamond.contractOwner(), onPlayersGameEnd); + uint256[] memory finalScores = gameId.closeGame(onPlayersGameEnd); address[] memory players = gameId.getPlayers(); emit GameOver(gameId, players, finalScores); } @@ -172,7 +170,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { /** * @dev Ends the current turn of a game with the provided game ID. `gameId` is the ID of the game. `votes` is the array of votes. * `newProposals` is the array of new proposals for the upcoming voting round. - * `proposerIndicies` is the array of indices of the proposers in the previous voting round. + * `proposerIndices` is the array of indices of the proposers in the previous voting round. * * emits a _ProposalScore_ event for each player if the turn is not the first. * emits a _TurnEnded_ event. @@ -189,18 +187,18 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { * - The game with `gameId` must have started. * - The game with `gameId` must not be over. * - newProposals array MUST be sorted randomly to ensure privacy - * votes and proposerIndicies MUST correspond to players array from game.getPlayers() + * votes and proposerIndices MUST correspond to players array from game.getPlayers() */ function endTurn( uint256 gameId, uint256[][] memory votes, string[] memory newProposals, //REFERRING TO UPCOMING VOTING ROUND - uint256[] memory proposerIndicies //REFERRING TO game.players index in PREVIOUS VOTING ROUND + uint256[] memory proposerIndices //REFERRING TO game.players index in PREVIOUS VOTING ROUND ) public { gameId.enforceIsGM(msg.sender); gameId.enforceHasStarted(); gameId.enforceIsNotOver(); - IRankifyInstanceCommons.RInstance storage game = gameId.getGameStorage(); + LibRankify.GameState storage game = gameId.getGameState(); uint256 turn = gameId.getTurn(); address[] memory players = gameId.getPlayers(); @@ -210,7 +208,7 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { votesSorted[player] = new uint256[](players.length); } for (uint256 votee = 0; votee < players.length; ++votee) { - uint256 voteesColumn = proposerIndicies[votee]; + uint256 voteesColumn = proposerIndices[votee]; if (voteesColumn < players.length) { // if index is above length of players array, it means the player did not propose for (uint256 voter = 0; voter < players.length; voter++) { @@ -219,14 +217,14 @@ contract RankifyInstanceGameMastersFacet is DiamondReentrancyGuard, EIP712 { } } - (, uint256[] memory roundScores) = gameId.calculateScoresQuadratic(votesSorted, proposerIndicies); + (, uint256[] memory roundScores) = gameId.calculateScoresQuadratic(votesSorted, proposerIndices); for (uint256 i = 0; i < players.length; ++i) { - string memory proposal = game.ongoingProposals[proposerIndicies[i]]; + string memory proposal = game.ongoingProposals[proposerIndices[i]]; emit ProposalScore(gameId, turn, proposal, proposal, roundScores[i]); } } (, uint256[] memory scores) = gameId.getScores(); - emit TurnEnded(gameId, gameId.getTurn(), players, scores, newProposals, proposerIndicies, votes); + emit TurnEnded(gameId, gameId.getTurn(), players, scores, newProposals, proposerIndices, votes); // Clean up game instance for upcoming round diff --git a/src/facets/RankifyInstanceGameOwnersFacet.sol b/src/facets/RankifyInstanceGameOwnersFacet.sol deleted file mode 100644 index 59ff6c49..00000000 --- a/src/facets/RankifyInstanceGameOwnersFacet.sol +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; -import {IRankifyInstanceCommons} from "../interfaces/IRankifyInstanceCommons.sol"; - -import "../abstracts/draft-EIP712Diamond.sol"; -import "../vendor/diamond/libraries/LibDiamond.sol"; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; - -error ZeroValue(); -error WrongAddress(); -error OutOfBounds(); - -contract RankifyInstanceGameOwnersFacet { - using LibTBG for LibTBG.GameInstance; - using LibTBG for uint256; - using LibTBG for LibTBG.GameSettings; - - function RInstanceStorage() internal pure returns (IRankifyInstanceCommons.RInstanceSettings storage bog) { - bytes32 position = LibTBG.getDataStorage(); - assembly { - bog.slot := position - } - } - - /** - * @dev Sets the game price. `newPrice` is the new game price. - * - * Modifies: - * - * - Sets the game price to `newPrice`. - * - * Requirements: - * - * - The caller must be the contract owner. - */ - function setGamePrice(uint256 newPrice) external { - LibDiamond.enforceIsContractOwner(); - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - _RInstance.gamePrice = newPrice; - } - - /** - * @dev Sets the join game price. `newPrice` is the new join game price. - * - * Modifies: - * - * - Sets the join game price to `newPrice`. - * - * Requirements: - * - * - The caller must be the contract owner. - */ - function setJoinGamePrice(uint256 newPrice) external { - LibDiamond.enforceIsContractOwner(); - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - _RInstance.joinGamePrice = newPrice; - } - - /** - * @dev Sets the rank token address. `newRankToken` is the new rank token address. - * - * Modifies: - * - * - Sets the rank token address to `newRankToken`. - * - * Requirements: - * - * - The caller must be the contract owner. - * - `newRankToken` must not be the zero address. - * - `newRankToken` must support the ERC1155 interface. - */ - function setRankTokenAddress(address newRankToken) external { - LibDiamond.enforceIsContractOwner(); - if (newRankToken == address(0)) { - require(false, "zerovalue"); //revert ZeroValue(); - } - if (!ERC165Checker.supportsInterface(newRankToken, type(IERC1155).interfaceId)) { - require(false, "wrongaddress"); //revert WrongAddress(); - } - - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - _RInstance.rankTokenAddress = newRankToken; - } - - /** - * - * @dev Sets the payment token address. - * Requirements: - * - * - The caller must be the contract owner. - * - `newPaymentToken` must not be the zero address. - * - `newRankToken` must support the ERC20 interface. - */ - function setPaymentTokenAddress(address newPaymentToken) external { - LibDiamond.enforceIsContractOwner(); - if (newPaymentToken == address(0)) { - require(false, "zerovalue"); //revert ZeroValue(); - } - if (!ERC165Checker.supportsInterface(newPaymentToken, type(IERC20).interfaceId)) { - require(false, "wrongaddress"); //revert WrongAddress(); - } - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - _RInstance.gamePaymentToken = newPaymentToken; - } - - /** - * @dev Sets the time per turn. `newTimePerTurn` is the new time per turn. - * - * Modifies: - * - * - Sets the time per turn to `newTimePerTurn`. - * - * Requirements: - * - * - The caller must be the contract owner. - */ - function setTimePerTurn(uint256 newTimePerTurn) external { - LibDiamond.enforceIsContractOwner(); - if (newTimePerTurn == 0) { - require(false, "zerovalue"); // revert ZeroValue(); - } - LibTBG.TBGStorageStruct storage tbg = LibTBG.TBGStorage(); - tbg.settings.timePerTurn = newTimePerTurn; - } - - /** - * @dev Sets the maximum number of players in a game. `newMaxPlayersSize` is the new maximum number of players. - * - * Modifies: - * - * - Sets the maximum number of players to `newMaxPlayersSize`. - * - * Requirements: - * - * - The caller must be the contract owner. - * - `newMaxPlayersSize` must be greater than or equal to the minimum number of players. - */ - function setMaxPlayersSize(uint256 newMaxPlayersSize) external { - LibDiamond.enforceIsContractOwner(); - LibTBG.TBGStorageStruct storage tbg = LibTBG.TBGStorage(); - if (newMaxPlayersSize < tbg.settings.minPlayersSize) { - require(false, "outofbonds"); // revert OutOfBounds(); - } - tbg.settings.maxPlayersSize = newMaxPlayersSize; - } - - /** - * @dev Sets the minimum number of players in a game. `newMinPlayersSize` is the new minimum number of players. - * - * Modifies: - * - * - Sets the minimum number of players to `newMinPlayersSize`. - * - * Requirements: - * - * - The caller must be the contract owner. - * - `newMinPlayersSize` must be less than or equal to the maximum number of players. - */ - function setMinPlayersSize(uint256 newMinPlayersSize) external { - LibDiamond.enforceIsContractOwner(); - LibTBG.TBGStorageStruct storage tbg = LibTBG.TBGStorage(); - if (newMinPlayersSize > tbg.settings.maxPlayersSize) { - require(false, "outofbonds"); // revert OutOfBounds(); - } - tbg.settings.minPlayersSize = newMinPlayersSize; - } - - /** - * @dev Sets the time to join a game. `newTimeToJoin` is the new time to join. - * - * Modifies: - * - * - Sets the time to join to `newTimeToJoin`. - * - * Requirements: - * - * - The caller must be the contract owner. - * - `newTimeToJoin` must not be zero. - */ - function setTimeToJoin(uint256 newTimeToJoin) external { - LibDiamond.enforceIsContractOwner(); - if (newTimeToJoin == 0) { - require(false, "ZeroValue"); //revert ZeroValue(); - } - LibTBG.TBGStorageStruct storage tbg = LibTBG.TBGStorage(); - tbg.settings.timeToJoin = newTimeToJoin; - } - - /** - * @dev Sets the maximum number of turns in a game. `newMaxTurns` is the new maximum number of turns. - * - * Modifies: - * - * - Sets the maximum number of turns to `newMaxTurns`. - * - * Requirements: - * - * - The caller must be the contract owner. - * - `newMaxTurns` must not be zero. - */ - function setMaxTurns(uint256 newMaxTurns) external { - LibDiamond.enforceIsContractOwner(); - if (newMaxTurns == 0) { - require(false, "ZeroValue"); // revert ZeroValue(); - } - LibTBG.TBGStorageStruct storage tbg = LibTBG.TBGStorage(); - tbg.settings.maxTurns = newMaxTurns; - } -} diff --git a/src/facets/RankifyInstanceMainFacet.sol b/src/facets/RankifyInstanceMainFacet.sol index 9cadd1f9..3a9cf6a9 100644 --- a/src/facets/RankifyInstanceMainFacet.sol +++ b/src/facets/RankifyInstanceMainFacet.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; -import {IRankifyInstanceCommons} from "../interfaces/IRankifyInstanceCommons.sol"; +import {IRankifyInstance} from "../interfaces/IRankifyInstance.sol"; import {IERC1155Receiver} from "../interfaces/IERC1155Receiver.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; @@ -13,63 +13,79 @@ import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "../abstracts/draft-EIP712Diamond.sol"; - import "hardhat/console.sol"; - +import {IErrors} from "../interfaces/IErrors.sol"; + +/** + * @title RankifyInstanceMainFacet + * @notice Main facet for the Rankify protocol that handles game creation and management + * @dev Implements core game functionality, ERC token receivers, and reentrancy protection + * @author Peeramid Labs, 2024 + */ contract RankifyInstanceMainFacet is - IRankifyInstanceCommons, + IRankifyInstance, IERC1155Receiver, DiamondReentrancyGuard, IERC721Receiver, - EIP712 + EIP712, + IErrors { - using LibTBG for LibTBG.GameInstance; + using LibTBG for LibTBG.Instance; using LibTBG for uint256; - using LibTBG for LibTBG.GameSettings; + using LibTBG for LibTBG.Settings; using LibRankify for uint256; - function RInstanceStorage() internal pure returns (RInstanceSettings storage bog) { - bytes32 position = LibTBG.getDataStorage(); - assembly { - bog.slot := position - } - } - /** - * @dev Creates a new game with the provided game master, game ID, and game rank. Optionally, additional ranks can be provided. `gameMaster` is the address of the game master. `gameId` is the ID of the new game. `gameRank` is the rank of the new game. `additionalRanks` is the array of additional ranks. - * - * emits a _GameCreated_ event. - * - * Requirements: - * There are some game price requirments that must be met under gameId.newGame function that are set during the contract initialization and refer to the contract maintainer benefits. - * - * Modifies: - * - * - Calls the `newGame` function with `gameMaster`, `gameRank`, and `msg.sender`. - * - Configures the coin vending with `gameId` and an empty configuration. - * - If `additionalRanks` is not empty, mints rank tokens for each additional rank and sets the additional ranks of the game with `gameId` to `additionalRanks`. + * @dev Internal function to create a new game with the specified parameters + * @param params Struct containing all necessary parameters for game creation + * @notice This function handles the core game creation logic, including: + * - Setting up the game state + * - Configuring the coin vending system + * - Emitting the game creation event */ - function createGame(address gameMaster, uint256 gameId, uint256 gameRank) public nonReentrant { - gameId.newGame(gameMaster, gameRank, msg.sender); + function createGame(LibRankify.NewGameParams memory params) private nonReentrant { + LibRankify.newGame(params); LibCoinVending.ConfigPosition memory emptyConfig; - LibCoinVending.configure(bytes32(gameId), emptyConfig); - emit gameCreated(gameId, gameMaster, msg.sender, gameRank); + LibCoinVending.configure(bytes32(params.gameId), emptyConfig); + emit gameCreated(params.gameId, params.gameMaster, msg.sender, params.gameRank); } - function createGame(address gameMaster, uint256 gameRank) public { + /** + * @dev External function to create a new game + * @param params Input parameters for creating a new game + * @notice This function: + * - Validates the contract is initialized + * - Processes input parameters + * - Creates a new game with specified settings + * @custom:security nonReentrant + */ + function createGame(IRankifyInstance.NewGameParamsInput memory params) public { LibRankify.enforceIsInitialized(); - RInstanceSettings storage settings = RInstanceStorage(); - createGame(gameMaster, settings.numGames + 1, gameRank); + LibRankify.InstanceState storage settings = LibRankify.instanceState(); + LibRankify.NewGameParams memory newGameParams = LibRankify.NewGameParams({ + gameId: settings.numGames + 1, + gameRank: params.gameRank, + creator: msg.sender, + minPlayerCnt: params.minPlayerCnt, + maxPlayerCnt: params.maxPlayerCnt, + gameMaster: params.gameMaster, + nTurns: params.nTurns, + voteCredits: params.voteCredits, + minGameTime: params.minGameTime, + timePerTurn: params.timePerTurn, + timeToJoin: params.timeToJoin + }); + + createGame(newGameParams); } /** * @dev Handles a player quitting a game with the provided game ID. `gameId` is the ID of the game. `player` is the address of the player. - * - * emits a _PlayerLeft_ event. - * - * Modifies: - * - * - Refunds the coins for `player` in the game with `gameId`. + * @param gameId The ID of the game. + * @param player The address of the player. + * @notice This function: + * - Refunds the coins for `player` in the game with `gameId`. + * - Emits a _PlayerLeft_ event. */ function onPlayerQuit(uint256 gameId, address player) private { LibCoinVending.refund(bytes32(gameId), player); @@ -78,55 +94,41 @@ contract RankifyInstanceMainFacet is /** * @dev Cancels a game with the provided game ID. `gameId` is the ID of the game. - * - * Modifies: - * - * - Calls the `enforceIsGameCreator` function with `msg.sender`. - * - * Requirements: - * - * - The caller must be the game creator of the game with `gameId`. - * - Game must not be started. + * @param gameId The ID of the game. + * @notice This function: + * - Calls the `enforceIsGameCreator` function with `msg.sender`. + * - Cancels the game. + * - Emits a _GameClosed_ event. + * @custom:security nonReentrant */ function cancelGame(uint256 gameId) public nonReentrant { gameId.enforceIsGameCreator(msg.sender); - gameId.cancelGame(onPlayerQuit, LibDiamond.contractOwner()); + gameId.cancelGame(onPlayerQuit); emit GameClosed(gameId); } /** * @dev Allows a player to leave a game with the provided game ID. `gameId` is the ID of the game. - * - * Modifies: - * - * - Calls the `quitGame` function with `msg.sender`, `true`, and `onPlayerQuit`. - * - * Requirements: - * - * - The caller must be a player in the game with `gameId`. - * - Game must not be started. + * @param gameId The ID of the game. + * @notice This function: + * - Calls the `quitGame` function with `msg.sender`, `true`, and `onPlayerQuit`. + * @custom:security nonReentrant */ function leaveGame(uint256 gameId) public nonReentrant { - gameId.quitGame(msg.sender, true, onPlayerQuit); + gameId.quitGame(msg.sender, onPlayerQuit); } /** * @dev Opens registration for a game with the provided game ID. `gameId` is the ID of the game. - * - * emits a _RegistrationOpen_ event. - * - * Modifies: - * - * - Calls the `enforceIsGameCreator` function with `msg.sender`. - * - Calls the `enforceIsPreRegistrationStage` function. - * - Calls the `openRegistration` function. - * - * Requirements: - * - * - The caller must be the game creator of the game with `gameId`. - * - The game with `gameId` must be in the pre-registration stage. + * @param gameId The ID of the game. + * @notice This function: + * - Calls the `enforceIsGameCreator` function with `msg.sender`. + * - Calls the `enforceIsPreRegistrationStage` function. + * - Calls the `openRegistration` function. + * - Emits a _RegistrationOpen_ event. */ function openRegistration(uint256 gameId) public { + gameId.enforceGameExists(); gameId.enforceIsGameCreator(msg.sender); gameId.enforceIsPreRegistrationStage(); gameId.openRegistration(); @@ -135,19 +137,12 @@ contract RankifyInstanceMainFacet is /** * @dev Allows a player to join a game with the provided game ID. `gameId` is the ID of the game. - * - * emits a _PlayerJoined_ event. - * - * Modifies: - * - * - Calls the `joinGame` function with `msg.sender`. - * - Calls the `fund` function with `bytes32(gameId)`. - * - * Requirements: - * - * - The caller must not be a player in the game with `gameId`. - * - Game phase must be registration. - * - Caller must be able to fulfill funding requirements. + * @param gameId The ID of the game. + * @notice This function: + * - Calls the `joinGame` function with `msg.sender`. + * - Calls the `fund` function with `bytes32(gameId)`. + * - Emits a _PlayerJoined_ event. + * @custom:security nonReentrant */ function joinGame(uint256 gameId) public payable nonReentrant { gameId.joinGame(msg.sender); @@ -157,17 +152,11 @@ contract RankifyInstanceMainFacet is /** * @dev Starts a game with the provided game ID early. `gameId` is the ID of the game. - * - * emits a _GameStarted_ event. - * - * Modifies: - * - * - Calls the `enforceGameExists` function. - * - Calls the `startGameEarly` function. - * - * Requirements: - * - * - The game with `gameId` must exist. + * @param gameId The ID of the game. + * @notice This function: + * - Calls the `enforceGameExists` function. + * - Calls the `startGameEarly` function. + * - Emits a _GameStarted_ event. */ function startGame(uint256 gameId) public { gameId.enforceGameExists(); @@ -216,70 +205,160 @@ contract RankifyInstanceMainFacet is return bytes4(""); } - function getContractState() public view returns (RInstanceState memory) { - RInstanceSettings storage settings = RInstanceStorage(); - LibTBG.GameSettings memory tbgSettings = LibTBG.getGameSettings(); - return (RInstanceState({BestOfState: settings, TBGSEttings: tbgSettings})); + /** + * @dev Returns the current state of the contract + * @return LibRankify.InstanceState The current state of the contract + */ + function getContractState() public pure returns (LibRankify.InstanceState memory) { + LibRankify.InstanceState memory state = LibRankify.instanceState(); + return state; } + /** + * @dev Returns the current turn of the game with the specified ID + * @param gameId The ID of the game + * @return uint256 The current turn of the game + */ function getTurn(uint256 gameId) public view returns (uint256) { return gameId.getTurn(); } + /** + * @dev Returns the game master of the game with the specified ID + * @param gameId The ID of the game + * @return address The game master of the game + */ function getGM(uint256 gameId) public view returns (address) { return gameId.getGM(); } + /** + * @dev Returns the scores of the game with the specified ID + * @param gameId The ID of the game + * @return address[] The players in the game + * @return uint256[] The scores of the players + */ function getScores(uint256 gameId) public view returns (address[] memory, uint256[] memory) { return gameId.getScores(); } + /** + * @dev Returns whether the game with the specified ID is in overtime + * @param gameId The ID of the game + * @return bool Whether the game is in overtime + */ function isOvertime(uint256 gameId) public view returns (bool) { return gameId.isOvertime(); } + /** + * @dev Returns whether the game with the specified ID is over + * @param gameId The ID of the game + * @return bool Whether the game is over + */ function isGameOver(uint256 gameId) public view returns (bool) { return gameId.isGameOver(); } + /** + * @dev Returns the game ID of the game that the specified player is in + * @param player The address of the player + * @return uint256 The ID of the game + */ function getPlayersGame(address player) public view returns (uint256) { return LibTBG.getPlayersGame(player); } + /** + * @dev Returns whether the game with the specified ID is in the last turn + * @param gameId The ID of the game + * @return bool Whether the game is in the last turn + */ function isLastTurn(uint256 gameId) public view returns (bool) { return gameId.isLastTurn(); } + /** + * @dev Returns whether registration is open for the game with the specified ID + * @param gameId The ID of the game + * @return bool Whether registration is open + */ function isRegistrationOpen(uint256 gameId) public view returns (bool) { return gameId.isRegistrationOpen(); } + /** + * @dev Returns the creator of the game with the specified ID + * @param gameId The ID of the game + * @return address The creator of the game + */ function gameCreator(uint256 gameId) public view returns (address) { - return gameId.getGameStorage().createdBy; + return gameId.getGameState().createdBy; } + /** + * @dev Returns the rank of the game with the specified ID + * @param gameId The ID of the game + * @return uint256 The rank of the game + */ function getGameRank(uint256 gameId) public view returns (uint256) { - return gameId.getGameStorage().rank; + return gameId.getGameState().rank; + } + + /** + * @dev Estimates the price of a game with the specified minimum game time + * @param minGameTime The minimum game time + * @return uint256 The estimated price of the game + */ + function estimateGamePrice(uint128 minGameTime) public pure returns (uint256) { + LibRankify.InstanceState memory state = LibRankify.instanceState(); + return LibRankify.getGamePrice(minGameTime, state.commonParams); } + /** + * @dev Returns the players in the game with the specified ID + * @param gameId The ID of the game + * @return address[] The players in the game + */ function getPlayers(uint256 gameId) public view returns (address[] memory) { return gameId.getPlayers(); } + /** + * @dev Returns whether the game with the specified ID can be started early + * @param gameId The ID of the game + * @return bool Whether the game can be started early + */ function canStartGame(uint256 gameId) public view returns (bool) { return gameId.canStartEarly(); } + /** + * @dev Returns whether the turn can be ended early for the game with the specified ID + * @param gameId The ID of the game + * @return bool Whether the turn can be ended early + */ function canEndTurn(uint256 gameId) public view returns (bool) { return gameId.canEndTurnEarly(); } + /** + * @dev Returns whether the player has completed their turn in the game with the specified ID + * @param gameId The ID of the game + * @param player The address of the player + * @return bool Whether the player has completed their turn + */ function isPlayerTurnComplete(uint256 gameId, address player) public view returns (bool) { return gameId.isPlayerTurnComplete(player); } + /** + * @dev Returns the voted array for the game with the specified ID + * @param gameId The ID of the game + * @return bool[] The voted array + */ function getPlayerVotedArray(uint256 gameId) public view returns (bool[] memory) { - IRankifyInstanceCommons.RInstance storage game = gameId.getGameStorage(); + LibRankify.GameState storage game = gameId.getGameState(); address[] memory players = gameId.getPlayers(); bool[] memory playerVoted = new bool[](players.length); for (uint256 i = 0; i < players.length; ++i) { @@ -288,8 +367,14 @@ contract RankifyInstanceMainFacet is return playerVoted; } + /** + * @dev Returns the players who have moved in the game with the specified ID + * @param gameId The ID of the game + * @return bool[] The players who have moved + * @return uint256 The number of players who have moved + */ function getPlayersMoved(uint256 gameId) public view returns (bool[] memory, uint256) { - LibTBG.GameInstance storage game = gameId._getGame(); + LibTBG.State storage game = gameId._getState(); address[] memory players = gameId.getPlayers(); bool[] memory playersMoved = new bool[](players.length); for (uint256 i = 0; i < players.length; ++i) { diff --git a/src/facets/RankifyInstanceRequirementsFacet.sol b/src/facets/RankifyInstanceRequirementsFacet.sol index fa0e1596..f68961d7 100644 --- a/src/facets/RankifyInstanceRequirementsFacet.sol +++ b/src/facets/RankifyInstanceRequirementsFacet.sol @@ -8,7 +8,7 @@ import {LibRankify} from "../libraries/LibRankify.sol"; contract RankifyInstanceRequirementsFacet { using LibTBG for uint256; using LibRankify for uint256; - using LibTBG for LibTBG.GameInstance; + using LibTBG for LibTBG.State; event RequirementsConfigured(uint256 indexed gameId, LibCoinVending.ConfigPosition config); /** diff --git a/src/initializers/MultipassInit.sol b/src/initializers/MultipassInit.sol deleted file mode 100644 index 4eadd655..00000000 --- a/src/initializers/MultipassInit.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/******************************************************************************\ -* Author: Nick Mudge (https://twitter.com/mudgen) -* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 -* -* Implementation of a diamond. -/******************************************************************************/ - -import {LibDiamond} from "../vendor/diamond/libraries/LibDiamond.sol"; -import {IDiamondLoupe} from "../vendor/diamond/interfaces/IDiamondLoupe.sol"; -import {IDiamondCut} from "../vendor/diamond/interfaces/IDiamondCut.sol"; -import {IERC173} from "../vendor/diamond/interfaces/IERC173.sol"; -import {IERC165} from "../vendor/diamond/interfaces/IERC165.sol"; -import {IMultipass} from "../interfaces/IMultipass.sol"; -import {LibEIP712WithStorage} from "../libraries/LibEIP712Storage.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "../modifiers/OnlyOwnerDiamond.sol"; - -// It is expected that this contract is customized if you want to deploy your diamond -// with data from a deployment script. Use the init function to initialize state variables -// of your diamond. Add parameters to the init funciton if you need to. - -contract MultipassInit is OnlyOwnerDiamond { - function _buildDomainSeparator( - bytes32 typeHash, - bytes32 nameHash, - bytes32 versionHash - ) private view returns (bytes32) { - return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); - } - - function init(string memory name, string memory version) external onlyOwner { - // adding ERC165 data - LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); - ds.supportedInterfaces[type(IERC165).interfaceId] = true; - ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true; - ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; - ds.supportedInterfaces[type(IERC173).interfaceId] = true; - - bytes32 hashedName = keccak256(bytes(name)); - bytes32 hashedVersion = keccak256(bytes(version)); - bytes32 typeHash = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - LibEIP712WithStorage.LibEIP712WithStorageStorage storage ss = LibEIP712WithStorage.EIP712WithStorage(); - ss._HASHED_NAME = hashedName; - ss._HASHED_VERSION = hashedVersion; - ss._CACHED_CHAIN_ID = block.chainid; - ss._CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); - ss._CACHED_THIS = address(this); - ss._TYPE_HASH = typeHash; - - ds.supportedInterfaces[type(IMultipass).interfaceId] = true; - - // add your own state variables - // EIP-2535 specifies that the `diamondCut` function takes two optional - // arguments: address _init and bytes calldata _calldata - // These arguments are used to execute an arbitrary function using delegatecall - // in order to set state variables in the diamond during deployment or an upgrade - // More info here: https://eips.ethereum.org/EIPS/eip-2535#diamond-interface - } -} diff --git a/src/initializers/RankifyInstanceInit.sol b/src/initializers/RankifyInstanceInit.sol index 12b75877..4fa822d1 100644 --- a/src/initializers/RankifyInstanceInit.sol +++ b/src/initializers/RankifyInstanceInit.sol @@ -15,17 +15,17 @@ import {IERC173} from "../vendor/diamond/interfaces/IERC173.sol"; import {IERC165} from "../vendor/diamond/interfaces/IERC165.sol"; import {LibEIP712WithStorage} from "../libraries/LibEIP712Storage.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {IRankifyInstanceCommons} from "../interfaces/IRankifyInstanceCommons.sol"; +import {IRankifyInstance} from "../interfaces/IRankifyInstance.sol"; import {IRankToken} from "../interfaces/IRankToken.sol"; import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; import {LibQuadraticVoting} from "../libraries/LibQuadraticVoting.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - +import {LibRankify} from "../libraries/LibRankify.sol"; // import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; // It is expected that this contract is customized if you want to deploy your diamond // with data from a deployment script. Use the init function to initialize state variables -// of your diamond. Add parameters to the init funciton if you need to. +// of your diamond. Add parameters to the init function if you need to. contract RankifyInstanceInit is Initializable { function _buildDomainSeparator( @@ -36,25 +36,12 @@ contract RankifyInstanceInit is Initializable { return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); } - function RInstanceStorage() internal pure returns (IRankifyInstanceCommons.RInstanceSettings storage bog) { - bytes32 position = LibTBG.getDataStorage(); - assembly { - bog.slot := position - } - } - struct contractInitializer { - uint256 timePerTurn; - uint256 maxPlayersSize; - uint256 minPlayersSize; address rewardToken; - uint256 timeToJoin; - uint256 gamePrice; - uint256 joinGamePrice; - uint256 maxTurns; - uint256 numWinners; - uint256 voteCredits; + uint256 principalCost; + uint96 principalTimeConstant; address paymentToken; + address beneficiary; } // You can add parameters to this function in order to pass in @@ -80,34 +67,22 @@ contract RankifyInstanceInit is Initializable { ss._CACHED_THIS = address(this); ss._TYPE_HASH = typeHash; - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - _RInstance.voting = LibQuadraticVoting.precomputeValues(initData.voteCredits, initData.minPlayersSize); - _RInstance.gamePrice = initData.gamePrice; - _RInstance.joinGamePrice = initData.joinGamePrice; + LibRankify.CommonParams storage commons = LibRankify.instanceState().commonParams; + + commons.principalCost = initData.principalCost; + commons.principalTimeConstant = initData.principalTimeConstant; + commons.gamePaymentToken = initData.paymentToken; + commons.rankTokenAddress = initData.rewardToken; + commons.beneficiary = initData.beneficiary; + + LibRankify.InstanceState storage _RInstance = LibRankify.instanceState(); require(initData.paymentToken != address(0), "initializer.paymentToken not set"); - _RInstance.gamePaymentToken = initData.paymentToken; + IRankToken rankContract = IRankToken(initData.rewardToken); require( rankContract.supportsInterface(type(IRankToken).interfaceId), "RankifyInstance->init: rank token address does not support Rank interface" ); - _RInstance.rankTokenAddress = initData.rewardToken; _RInstance.contractInitialized = true; - - LibTBG.GameSettings memory settings; - settings.timePerTurn = initData.timePerTurn; - settings.maxPlayersSize = initData.maxPlayersSize; - settings.minPlayersSize = initData.minPlayersSize; - settings.timeToJoin = initData.timeToJoin; - settings.maxTurns = initData.maxTurns; - settings.numWinners = initData.numWinners; - LibTBG.init(settings); - - // add your own state variables - // EIP-2535 specifies that the `diamondCut` function takes two optional - // arguments: address _init and bytes calldata _calldata - // These arguments are used to execute an arbitrary function using delegatecall - // in order to set state variables in the diamond during deployment or an upgrade - // More info here: https://eips.ethereum.org/EIPS/eip-2535#diamond-interface } } diff --git a/src/initializers/TournamentDistributionInitializer.sol b/src/initializers/TournamentDistributionInitializer.sol deleted file mode 100644 index ce02732e..00000000 --- a/src/initializers/TournamentDistributionInitializer.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@peeramid-labs/eds/src/interfaces/IInitializer.sol"; -import "../initializers/RankifyInstanceInit.sol"; -import "@peeramid-labs/eds/src/abstracts/CodeIndexer.sol"; -import "@peeramid-labs/eds/src/libraries/LibSemver.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -contract TournamentDistributionInitializer is IInitializer, CodeIndexer, Initializable { - address private immutable paymentToken; - address private immutable rewardToken; - - uint256 private immutable gamePrice; - uint256 private immutable joinGamePrice; - - constructor(address _paymentToken, address _rewardToken, uint256 _gamePrice, uint256 _joinGamePrice) { - paymentToken = _paymentToken; - rewardToken = _rewardToken; - gamePrice = _gamePrice; - joinGamePrice = _joinGamePrice; - } - - struct userSettings { - uint256 timePerTurn; - uint256 maxPlayersSize; - uint256 minPlayersSize; - uint256 timeToJoin; - uint256 maxTurns; - uint256 voteCredits; - } - - function initialize( - bytes32, - address[] memory instances, - bytes32 distributionName, - uint256 distributionVersion, - bytes calldata args - ) external override { - if (instances.length < 3) { - revert("This initializer needs an instance, payment and rank tokens in order to work"); - } - - RankifyInstanceInit initializerFacet = RankifyInstanceInit(instances[0]); - userSettings memory userConfig = abi.decode(args, (userSettings)); - RankifyInstanceInit.contractInitializer memory initializer = RankifyInstanceInit.contractInitializer({ - timePerTurn: userConfig.timePerTurn, - maxPlayersSize: userConfig.maxPlayersSize, - minPlayersSize: userConfig.minPlayersSize, - rewardToken: rewardToken, - timeToJoin: userConfig.timeToJoin, - gamePrice: gamePrice, - joinGamePrice: joinGamePrice, - maxTurns: userConfig.maxTurns, - numWinners: 1, - voteCredits: userConfig.voteCredits, - paymentToken: paymentToken - }); - initializerFacet.init( - string(abi.encodePacked(distributionName)), - LibSemver.toString(LibSemver.parse(distributionVersion)), - initializer - ); - } -} diff --git a/src/interfaces/IErrors.sol b/src/interfaces/IErrors.sol new file mode 100644 index 00000000..09644fa1 --- /dev/null +++ b/src/interfaces/IErrors.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IErrors { + error invalidConfiguration(string message); + error zeroValue(); +} diff --git a/src/interfaces/ILockableERC1155.sol b/src/interfaces/ILockableERC1155.sol index 071320e5..53b15ca8 100644 --- a/src/interfaces/ILockableERC1155.sol +++ b/src/interfaces/ILockableERC1155.sol @@ -36,4 +36,6 @@ interface ILockableERC1155 is IERC1155 { * - The unlocked balance of tokens. */ function unlockedBalanceOf(address account, uint256 id) external view returns (uint256); + + function burn(address account, uint256 id, uint256 value) external; } diff --git a/src/interfaces/IMultipass.sol b/src/interfaces/IMultipass.sol deleted file mode 100644 index e090b2d2..00000000 --- a/src/interfaces/IMultipass.sol +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../libraries/LibMultipass.sol"; - -interface IMultipass { - function resolveRecord( - LibMultipass.NameQuery memory query - ) external view returns (bool, LibMultipass.Record memory); - - /** @dev same as resolveRecord but returns username, id and LibMultipass.Domain as string */ - // function resolveRecordToString(LibMultipass.NameQuery memory query) - // external - // view - // returns ( - // bool, - // LibMultipass.Record memory - // ); - - /** - * @dev Initializes new LibMultipass.Domain and configures it's parameters - * - * Requirements: - * registrar is not zero - * domainName is not empty - * domainIndex is either zero(auto assign) or can be one of preoccupied LibMultipass.Domain names - * domainName does not exist yet - * onlyOwner - * referrerReward+referralDiscount cannot be larger than fee - * @param registrar address of registrar - * @param freeRegistrationsNumber number of registrations free of fee - * @param fee fee in base currency of network - * @param domainName name of LibMultipass.Domain - * @param referrerReward referral fee share in base currency of network - * @param referralDiscount referral discount in base currency of network - * - * Emits an {InitializedDomain} event. - */ - function initializeDomain( - address registrar, - uint256 freeRegistrationsNumber, - uint256 fee, - bytes32 domainName, - uint256 referrerReward, - uint256 referralDiscount - ) external; - - /** - * @dev Activates LibMultipass.Domain name - * - * Requirements: - * msg.sender is Owner - * - * - * Emits an {DomainActivated} event. - */ - function activateDomain(bytes32 domainName) external; - - /** - * @dev Deactivates LibMultipass.Domain name - * - * Deactivated LibMultipass.Domain cannot mutate names and will return zeros - * - * Requirements: - * msg.sender is Owner OR registrar - * - * - * Emits an {DomainDeactivated} event. - */ - - function deactivateDomain(bytes32 domainName) external; - - /** - * @dev Changes registrar address - * - * Requirements: - * msg.sender is Owner - * - * Emits an {DomainFeeChanged} event. - */ - function changeFee(bytes32 domainName, uint256 fee) external; - - /** - * @dev Changes registrar address - * - * Requirements: - * msg.sender is Owner - * - * Emits an {RegistrarChangeRequested} event. - */ - function changeRegistrar(bytes32 domainName, address newRegistrar) external; - - /** - * @dev deletes name - * - * Requirements: - * msg.sender is Owner - * - * Emits an {DomainTTLChangeRequested} event. - */ - function deleteName(LibMultipass.NameQuery memory query) external; - - /** - * @dev executes all pending changes to LibMultipass.Domain that fulfill TTL - * - * Requirements: - * domainName must be set - * referrerFeeShare+referralDiscount cannot be larger than 2^32 - * - * - * Emits an {ReferralProgramChangeRequested} event. - */ - function changeReferralProgram( - uint256 referrerFeeShare, - uint256 referralDiscount, - uint256 freeRegistrations, - bytes32 domainName - ) external; - - /** - * @dev registers new name under LibMultipass.Domain - * - * Requirements: - * all arguments must be set - * domainName must be active - * resolveRecord for given arguments should return no LibMultipass.Record - * - * - * Emits an {registered} event. - */ - function register( - LibMultipass.Record memory newRecord, - bytes32 domainName, - bytes memory registrarSignature, - uint256 signatureDeadline, - LibMultipass.NameQuery memory referrer, - bytes memory referralCode - ) external payable; - - /** - * @dev modifies exsisting LibMultipass.Record - * - * Requirements: - * resolveRecord for given arguments should return valid LibMultipass.Record - * LibMultipass.Domain must be active - * newAddress and newName should be set and be unique in current LibMultipass.Domain - * - * @param domainName LibMultipass.Domain - * @param newName new name - * - * Emits an {Modified} event. - */ - function modifyUserName( - bytes32 domainName, - LibMultipass.NameQuery memory query, - bytes32 newName, - bytes memory registrarSignature, - uint256 signatureDeadline - ) external payable; - - /** - * @dev returns balance of this contract - */ - function getBalance() external view returns (uint256); - - /** - * @dev returns LibMultipass.Domain state variables - * @param domainName name of the LibMultipass.Domain - * @return (name, - fee, - freeRegistrationsNumber, - referrerReward, - referralDiscount, - isActive, - registrar, - ttl, - registerSize) - */ - function getDomainState(bytes32 domainName) external view returns (LibMultipass.Domain memory); - - /** - * @dev returns contract state variables - - * @return (s_numDomains) - */ - function getContractState() external view returns (uint256); - - /** - * @dev Withraws funds stored in smart contract - * - * Requirements: - * onlyOwner - * - * Emits an {fundsWithdawn} event. - */ - function withrawFunds(address to) external; - - function getModifyPrice(LibMultipass.NameQuery memory query) external view returns (uint256); - - event fundsWithdawn(uint256 indexed amount, address indexed account); - - // event InitializedDomain(uint256 indexed index, bytes32 indexed domainName); - event InitializedDomain( - address indexed registrar, - uint256 freeRegistrationsNumber, - uint256 indexed fee, - bytes32 indexed domainName, - uint256 referrerReward, - uint256 referralDiscount - ); - event DomainActivated(bytes32 indexed domainName); - event DomainDeactivated(bytes32 indexed domainName); - - event DomainFeeChanged(bytes32 indexed domainName, uint256 indexed newFee); - event FreeRegistrationsChanged(uint256 indexed domainIndex, uint256 indexed newAmount); - - event RegistrarChangeRequested(bytes32 indexed domainName, address indexed registrar); - event DomainNameChangeRequested(uint256 indexed domainIndex, bytes32 indexed NewDomainName); - event nameDeleted(bytes32 indexed domainName, address indexed wallet, bytes32 indexed id, bytes32 name); - event DomainTTLChangeRequested(bytes32 indexed domainName, uint256 amount); - event ReferralProgramChanged( - bytes32 indexed domainName, - uint256 reward, - uint256 discount, - uint256 indexed freeNumber - ); - event DomainChangesAreLive(bytes32 indexed domainName, bytes32[] indexed changes); - event changesQeueCanceled(bytes32 indexed domainName, bytes32[] indexed changes); - - event Registered(bytes32 indexed domainName, LibMultipass.Record NewRecord); - - event Referred(LibMultipass.Record refferrer, LibMultipass.Record newRecord, bytes32 indexed domainName); - - event UserRecordModified( - LibMultipass.Record indexed newRecord, - bytes32 indexed oldName, - bytes32 indexed domainName - ); -} diff --git a/src/interfaces/IRankifyInstance.sol b/src/interfaces/IRankifyInstance.sol new file mode 100644 index 00000000..471b2597 --- /dev/null +++ b/src/interfaces/IRankifyInstance.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; +import {LibQuadraticVoting} from "../libraries/LibQuadraticVoting.sol"; + +interface IRankifyInstance { + error NoDivisionReminderAllowed(uint256 a, uint256 b); + error invalidTurnCount(uint256 nTurns); + error RankNotSpecified(); + + event RegistrationOpen(uint256 indexed gameId); + event PlayerJoined(uint256 indexed gameId, address participant); + event GameStarted(uint256 indexed gameId); + event gameCreated(uint256 gameId, address indexed gm, address indexed creator, uint256 indexed rank); + event GameClosed(uint256 indexed gameId); + event PlayerLeft(uint256 indexed gameId, address indexed player); + + struct NewGameParamsInput { + uint256 gameRank; + uint256 minPlayerCnt; + uint256 maxPlayerCnt; + uint96 nTurns; + uint256 voteCredits; + address gameMaster; + uint128 minGameTime; + uint128 timePerTurn; + uint128 timeToJoin; + } +} diff --git a/src/interfaces/IRankifyInstanceCommons.sol b/src/interfaces/IRankifyInstanceCommons.sol index 08c66f52..a1e47040 100644 --- a/src/interfaces/IRankifyInstanceCommons.sol +++ b/src/interfaces/IRankifyInstanceCommons.sol @@ -4,44 +4,8 @@ import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; import {LibQuadraticVoting} from "../libraries/LibQuadraticVoting.sol"; -interface IRankifyInstanceCommons { - struct RInstanceSettings { - uint256 gamePrice; - address gamePaymentToken; - uint256 joinGamePrice; - uint256 numGames; - address rankTokenAddress; - bool contractInitialized; - LibQuadraticVoting.qVotingStruct voting; - } - - struct RInstanceState { - RInstanceSettings BestOfState; - LibTBG.GameSettings TBGSEttings; - } - - struct VoteHidden { - bytes32 hash; - bytes proof; - } - - struct RInstance { - uint256 rank; - address createdBy; - mapping(uint256 => string) ongoingProposals; //Previous Turn Proposals (These are being voted on) - uint256 numOngoingProposals; - uint256 numPrevProposals; - mapping(address => bytes32) proposalCommitmentHashes; //Current turn Proposal submittion - uint256 numCommitments; - mapping(address => VoteHidden) votesHidden; - address[] additionalRanks; - uint256 paymentsBalance; - uint256 numVotesThisTurn; - uint256 numVotesPrevTurn; - mapping(address => bool) playerVoted; - } - - event RegistrationOpen(uint256 indexed gameid); +interface IRankifyInstance { + event RegistrationOpen(uint256 indexed gameId); event PlayerJoined(uint256 indexed gameId, address participant); event GameStarted(uint256 indexed gameId); event gameCreated(uint256 gameId, address indexed gm, address indexed creator, uint256 indexed rank); diff --git a/src/libraries/LibCoinVending.sol b/src/libraries/LibCoinVending.sol index 0c757f0a..5137287f 100644 --- a/src/libraries/LibCoinVending.sol +++ b/src/libraries/LibCoinVending.sol @@ -9,11 +9,11 @@ import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; /** * @dev This library is used to simulate the vending machine coin acceptor state machine that: - * - Supports large number of positions; Each represents requirements to acess different goods of the virtual vending machine. + * - Supports large number of positions; Each represents requirements to access different goods of the virtual vending machine. * - Accepts multiple assets of following types: Native (Eth), ERC20, ERC721, and ERC1155 tokens that can be stacked together. * - Allows for each individual asset action promise can be one of following: * - Lock: The asset is locked in the acceptor with promise that asset will be returned to the sender at release funds time. - * - Bet: The asset is locked in the acceptor with promise that asset will be awarded to benificiary at release funds time. + * - Bet: The asset is locked in the acceptor with promise that asset will be awarded to beneficiary at release funds time. * - Pay: The asset is locked in the acceptor with promise that asset will be paid to payee at release funds time. * - Burn: The asset is locked in the acceptor with promise that asset will be destroyed at release funds time. * - Maintains each position balance, hence allowing multiple participants to line up for the same position. @@ -28,7 +28,7 @@ import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; * - Returning locked assets back to sender * * This library DOES enforces that any position can only be refunded or processed only within amount funded boundaries - * This library DOES NOT store the addresses of senders, nor benificiaries, nor payees. + * This library DOES NOT store the addresses of senders, nor beneficiaries, nor payees. * This is to be stored within implementation contract. * * @@ -42,13 +42,13 @@ import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; * 1. fund position with assets via fund(...) * 2. release or refund assets via release(...) or refund(...) * 3. repeat steps 1 and 2 as needed. - * Position can be recofigured at any time when it's effective balance is zero: `timesFunded - timesRefuned - timesReleased = 0` + * Position can be reconfigured at any time when it's effective balance is zero: `timesFunded - timesRefund - timesReleased = 0` * * * Test state: * This library most functionality has been tested: see ../tests/LibCoinVending.ts and ../tests/report.md for details. * - * ERC721 token is checked only for "HAVE" condition since putting requirements on non fungable token id yet to be resolved. + * ERC721 token is checked only for "HAVE" condition since putting requirements on non fungible token id yet to be resolved. * (see ERC721 section in the code below) * * This library has not been yet audited diff --git a/src/libraries/LibMultipass.sol b/src/libraries/LibMultipass.sol deleted file mode 100644 index b6c09436..00000000 --- a/src/libraries/LibMultipass.sol +++ /dev/null @@ -1,276 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -// import "./LibDiamondOwner.sol"; -// import { IMultipass } from "../interfaces/sol"; -import "hardhat/console.sol"; -import "@openzeppelin/contracts/utils/math/Math.sol"; - -library LibMultipass { - /** - * @dev resolves user from any given argument - * Requirements: - * domainName must be given and must be initialized - * id OR username OR address must be given - * This method first tries to resolve by address, then by user id and finally by username - * @param domainName domain name - * @param wallet adress of user - * @param id user id - * @param username username - * @param targetDomain if this is set to valid domain name, then after sucessfull resolving account at domainName, - * this method will rerun with resolving user properties in targetDomain - */ - struct NameQuery { - bytes32 domainName; - address wallet; - bytes32 name; - bytes32 id; - bytes32 targetDomain; - } - - /** - * @dev The domain name of the registrar. - * @param registrar is the address private key of which is owned by signing server (e.g. Discord bot server) - * @param name is unique string that is used to find this domain within domains. - * @param freeRegistrationsNumber is the number of free registrations for this domain - - * @param fee amount of payment requried to register name in the domain - * @param ttl time to live for changes in the domain properties - * @param isActive when is false domain name will not respond to any changes and will not return any address - **/ - struct Domain { - bytes32 name; //32bytes - uint256 fee; //32bytes - uint256 freeRegistrationsNumber; //32bytes - uint256 referrerReward; //32bytes - uint256 referralDiscount; //32bytes - bool isActive; //1byte - address registrar; //20 bytes - uint24 ttl; //3 bytes (not being used for now) - uint256 registerSize; //32bytes - } - - // struct NameQueryBytes32 { - // string domainName; - // address wallet; - // bytes32 name; - // bytes32 id; - // string targetDomain; - // } - struct Record { - address wallet; - bytes32 name; - bytes32 id; - uint96 nonce; - bytes32 domainName; - } - - // struct RecordBytes32 { - // address wallet; - // bytes32 name; - // bytes32 id; - // uint96 nonce; - // } - - bytes32 constant MULTIPASS_STORAGE_POSITION = keccak256("multipass.diamond.storage.position"); - - /** - * @dev The domain name of the registrar. - * @param properties - domain configuration - * @param idToAddress is mapping from unique identificator to an address - * @param registerSize is number of registered users for this domain - * @param nonce is incremented each time Record changes in addressToId map - * @param nameToId is mapping from names to unique identificator. While each name required to be unique, - names might change on the domain, so we keep records to user identificators as immutable property of user - * @param addressToId is mapping from an address to unique identificator - * @param idToName is mapping from identificator to a name - **/ - struct DomainNameService { - Domain properties; //128 bytes - mapping(bytes32 => address) idToAddress; //N*20bytes - mapping(bytes32 => uint96) nonce; //N*12bytes - mapping(address => bytes32) addressToId; //N*32 bytes - mapping(bytes32 => bytes32) nameToId; //N*32 bytes - mapping(bytes32 => bytes32) idToName; //N*32 bytes - //Total: 128+N*160 Bytes - } - - struct MultipassStorageStruct { - mapping(uint256 => DomainNameService) domains; - mapping(bytes32 => uint256) domainNameToIndex; //helper to get domain index by name - uint256 numDomains; - } - - function MultipassStorage() internal pure returns (MultipassStorageStruct storage es) { - bytes32 position = MULTIPASS_STORAGE_POSITION; - assembly { - es.slot := position - } - } - - bytes32 internal constant _TYPEHASH = - keccak256("registerName(bytes32 name,bytes32 id,bytes32 domainName,uint256 deadline,uint96 nonce)"); - bytes32 internal constant _TYPEHASH_REFERRAL = keccak256("proofOfReferrer(address referrerAddress)"); - - // function _stringToBytes32(string memory source) internal pure returns (bytes32 result) { - // uint256 length = bytes(source).length; - // require(length <= 32, "_stringToBytes32->String longer than 32 bytes"); - // bytes memory tempEmptyStringTest = abi.encodePacked(source); - // if (tempEmptyStringTest.length == 0) { - // return 0x0; - // } - // assembly { - // result := mload(add(source,32)) - // } - // } - - function _checkStringFits32b(string memory value) internal pure returns (bool) { - if (bytes(value).length <= 32) { - return true; - } else { - return false; - } - } - - function _checkNotEmpty(bytes32 value) internal pure returns (bool) { - if (value == "") { - return false; - } else { - return true; - } - } - - function resolveDomainIndex(bytes32 domainName) internal view returns (uint256) { - MultipassStorageStruct storage s = MultipassStorage(); - return s.domainNameToIndex[domainName]; - } - - function _getDomainStorage(bytes32 domainName) internal view returns (DomainNameService storage) { - MultipassStorageStruct storage s = MultipassStorage(); - - return s.domains[resolveDomainIndex(domainName)]; - } - - function _initializeDomain( - address registrar, - uint256 freeRegistrationsNumber, - uint256 fee, - bytes32 domainName, - uint256 referrerReward, - uint256 referralDiscount - ) internal { - LibMultipass.MultipassStorageStruct storage ms = LibMultipass.MultipassStorage(); - - uint256 domainIndex = ms.numDomains + 1; - LibMultipass.DomainNameService storage _domain = ms.domains[domainIndex]; - _domain.properties.registrar = registrar; - _domain.properties.freeRegistrationsNumber = freeRegistrationsNumber; - _domain.properties.fee = fee; - _domain.properties.name = domainName; - _domain.properties.referrerReward = referrerReward; - _domain.properties.referralDiscount = referralDiscount; - ms.numDomains++; - ms.domainNameToIndex[domainName] = domainIndex; - } - - function _getModifyPrice(LibMultipass.Record memory userRecord) internal view returns (uint256) { - LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(userRecord.domainName); - uint256 feeCoefficient = _domain.properties.fee / 10; - uint256 nonceCoefficient = userRecord.nonce * userRecord.nonce; - return ((feeCoefficient * nonceCoefficient) + _domain.properties.fee); - } - - function _resolveRecord(NameQuery memory query) private view returns (bool, Record memory) { - if ((query.wallet == address(0)) && (query.id == bytes32(0)) && (query.name == bytes32(0))) { - Record memory rv; - return (false, rv); - } - - MultipassStorageStruct storage s = MultipassStorage(); - DomainNameService storage _domain = s.domains[s.domainNameToIndex[query.domainName]]; - DomainNameService storage _targetDomain = s.domains[ - s.domainNameToIndex[query.targetDomain == bytes32(0) ? query.domainName : query.targetDomain] - ]; - address _wallet; - { - // resolve wallet - if (query.wallet != address(0)) { - _wallet = query.wallet; - } else if (query.id != bytes32(0)) { - _wallet = _domain.idToAddress[query.id]; - } else if (query.name != bytes32(0)) { - bytes32 _id = _domain.nameToId[query.name]; - _wallet = _domain.idToAddress[_id]; - } - } - - //from wallet find and return record - return _resolveFromAddress(_wallet, _targetDomain); - } - - /** - @dev resolves Record of name query in to status and identity */ - function resolveRecord(NameQuery memory query) internal view returns (bool, Record memory) { - return _resolveRecord(query); - } - - /** @dev this function bears no security checks, it will ignore nonce in arg and will increment - * nonce value stored in domain instread - */ - function _setRecord(DomainNameService storage domain, Record memory record) internal { - domain.addressToId[record.wallet] = record.id; - domain.idToAddress[record.id] = record.wallet; - domain.idToName[record.id] = record.name; - domain.nameToId[record.name] = record.id; - domain.nonce[record.id] += 1; - } - - function _resolveFromAddress( - address _address, - DomainNameService storage _domain - ) private view returns (bool, Record memory) { - Record memory resolved; - - resolved.id = _domain.addressToId[_address]; - resolved.name = _domain.idToName[resolved.id]; - resolved.nonce = _domain.nonce[resolved.id]; - resolved.wallet = _address; - resolved.domainName = _domain.properties.name; - - if (resolved.id == bytes32(0)) { - return (false, resolved); - } - return (true, resolved); - } - - function queryFromRecord(Record memory _record, bytes32 _domainName) internal pure returns (NameQuery memory) { - NameQuery memory _query; - _query.id = _record.id; - _query.domainName = _domainName; - _query.name = _record.name; - _query.wallet = _record.wallet; - return _query; - } - - function shouldRegisterForFree(DomainNameService storage domain) internal view returns (bool) { - return domain.properties.freeRegistrationsNumber > domain.properties.registerSize ? true : false; - } - - function _registerNew(Record memory newRecord, DomainNameService storage domain) internal { - _setRecord(domain, newRecord); - domain.properties.registerSize += 1; - } - - function _getContractState() internal view returns (uint256) { - LibMultipass.MultipassStorageStruct storage ms = LibMultipass.MultipassStorage(); - return ms.numDomains; - } - - function _getDomainStorageByIdx(uint256 index) internal view returns (DomainNameService storage) { - MultipassStorageStruct storage s = MultipassStorage(); - - return s.domains[index]; - } - - using LibMultipass for NameQuery; -} diff --git a/src/libraries/LibQuadraticVoting.sol b/src/libraries/LibQuadraticVoting.sol index 5e46e980..65fb722e 100644 --- a/src/libraries/LibQuadraticVoting.sol +++ b/src/libraries/LibQuadraticVoting.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; -error quadraticVotingError(string paramter, uint256 arg, uint256 arg2); +error quadraticVotingError(string parameter, uint256 arg, uint256 arg2); /** * @title LibQuadraticVoting @@ -12,7 +12,7 @@ library LibQuadraticVoting { struct qVotingStruct { uint256 voteCredits; uint256 maxQuadraticPoints; - uint256 minQuadraticPositons; + uint256 minQuadraticPositions; } /** @@ -39,13 +39,13 @@ library LibQuadraticVoting { } while (accumulator < voteCredits); // This enforces requirement that all vote credits can indeed be spended (no leftovers) if (accumulator != voteCredits) require(false, "quadraticVotingError: voteCredits bust be i^2 series"); //revert quadraticVotingError("voteCredits bust be i^2 series", accumulator, voteCredits); - q.minQuadraticPositons = iterator; - // In order to spend all vote credits there must be at least minQuadraticPositons+1 (becuase proposer is also a player and cannot vote for himself) - if (minExpectedVoteItems <= q.minQuadraticPositons) + q.minQuadraticPositions = iterator; + // In order to spend all vote credits there must be at least minQuadraticPositions+1 (becuase proposer is also a player and cannot vote for himself) + if (minExpectedVoteItems <= q.minQuadraticPositions) require(false, "quadraticVotingError: Minimum Voting positions above min players"); // revert quadraticVotingError( // "Minimum Voting positions above min players", - // q.minQuadraticPositons, + // q.minQuadraticPositions, // minExpectedVoteItems // ); q.voteCredits = voteCredits; @@ -63,7 +63,7 @@ library LibQuadraticVoting { qVotingStruct memory q, uint256[][] memory VotersVotes, bool[] memory voterVoted, - uint256 notVotedGivesEveyone, + uint256 notVotedGivesEveryone, uint256 proposalsLength ) internal pure returns (uint256[] memory) { uint256[] memory scores = new uint256[](proposalsLength); @@ -77,13 +77,16 @@ library LibQuadraticVoting { uint256[] memory voterVotes = VotersVotes[vi]; if (!voterVoted[vi]) { // Check if voter wasn't voting - scores[proposalIdx] += notVotedGivesEveyone; // Gives benefits to everyone but himself + scores[proposalIdx] += notVotedGivesEveryone; // Gives benefits to everyone but himself creditsUsed[vi] = q.voteCredits; } else { //If voter voted scores[proposalIdx] += voterVotes[proposalIdx]; creditsUsed[vi] += voterVotes[proposalIdx] ** 2; - if (creditsUsed[vi] > q.voteCredits) require(false, "quadraticVotingError"); // revert quadraticVotingError("Quadratic: vote credits overrun", q.voteCredits, creditsUsed[vi]); + require( + creditsUsed[vi] <= q.voteCredits, + quadraticVotingError("Quadratic: vote credits overrun", q.voteCredits, creditsUsed[vi]) + ); } } } diff --git a/src/libraries/LibRankify.sol b/src/libraries/LibRankify.sol index a42eeb0b..052214b0 100644 --- a/src/libraries/LibRankify.sol +++ b/src/libraries/LibRankify.sol @@ -1,19 +1,94 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; import {LibTBG} from "../libraries/LibTurnBasedGame.sol"; -import {IRankifyInstanceCommons} from "../interfaces/IRankifyInstanceCommons.sol"; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IRankifyInstance} from "../interfaces/IRankifyInstance.sol"; import {IRankToken} from "../interfaces/IRankToken.sol"; -import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; +import "../tokens/Rankify.sol"; import {LibQuadraticVoting} from "./LibQuadraticVoting.sol"; import "hardhat/console.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol"; +/** + * @title LibRankify + * @dev Core library for the Rankify protocol that handles game state management, voting, and player interactions + * @author Peeramid Labs, 2024 + */ library LibRankify { - using LibTBG for LibTBG.GameInstance; + using LibTBG for LibTBG.Instance; using LibTBG for uint256; - using LibTBG for LibTBG.GameSettings; + using LibTBG for LibTBG.Settings; + using LibTBG for LibTBG.State; using LibQuadraticVoting for LibQuadraticVoting.qVotingStruct; + /** + * @dev Main state structure for a Rankify instance + * @param numGames Total number of games created in this instance + * @param contractInitialized Whether the contract has been properly initialized + * @param commonParams Common parameters shared across all games in this instance + */ + struct InstanceState { + uint256 numGames; + bool contractInitialized; + CommonParams commonParams; + } + + /** + * @dev Common parameters shared across all games in a Rankify instance + * @param principalCost Base cost for creating a game + * @param principalTimeConstant Time constant used for game duration calculations + * @param gamePaymentToken Address of the token used for game payments + * @param rankTokenAddress Address of the rank token contract + * @param beneficiary Address that receives a portion of game fees + */ + struct CommonParams { + uint256 principalCost; + uint96 principalTimeConstant; + address gamePaymentToken; + address rankTokenAddress; + address beneficiary; + } + + /** + * @dev Structure for storing hidden votes with their proof + * @param hash Hash of the vote + * @param proof Cryptographic proof associated with the vote + */ + struct VoteHidden { + bytes32 hash; + bytes proof; + } + + /** + * @dev Comprehensive state structure for an individual game + * @param gamePrice Price paid to create this game + * @param rank Required rank level for participation + * @param minGameTime Minimum duration the game must run + * @param createdBy Address of the game creator + * @param numOngoingProposals Number of active proposals + * @param numPrevProposals Number of completed proposals + * @param numCommitments Number of vote commitments received + * @param numVotesThisTurn Vote count in current turn + * @param numVotesPrevTurn Vote count from previous turn + * @param voting Quadratic voting state for this game + */ + struct GameState { + uint256 gamePrice; + uint256 rank; + uint256 minGameTime; + address createdBy; + uint256 numOngoingProposals; + uint256 numPrevProposals; + uint256 numCommitments; + uint256 numVotesThisTurn; + uint256 numVotesPrevTurn; + LibQuadraticVoting.qVotingStruct voting; + mapping(uint256 => string) ongoingProposals; //Previous Turn Proposals (These are being voted on) + mapping(address => bytes32) proposalCommitmentHashes; //Current turn Proposal submission + mapping(address => VoteHidden) votesHidden; + mapping(address => bool) playerVoted; + } + /** * @dev Compares two strings for equality. `a` and `b` are the strings to compare. * @@ -32,7 +107,7 @@ library LibRankify { * * - The game storage for `gameId`. */ - function getGameStorage(uint256 gameId) internal view returns (IRankifyInstanceCommons.RInstance storage game) { + function getGameState(uint256 gameId) internal view returns (GameState storage game) { bytes32 position = LibTBG.getGameDataStorage(gameId); assembly { game.slot := position @@ -44,12 +119,12 @@ library LibRankify { * * Returns: * - * - The RInstanceSettings storage. + * - The instanceState storage. */ - function RInstanceStorage() internal pure returns (IRankifyInstanceCommons.RInstanceSettings storage bog) { + function instanceState() internal pure returns (InstanceState storage contractState) { bytes32 position = LibTBG.getDataStorage(); assembly { - bog.slot := position + contractState.slot := position } } @@ -68,7 +143,7 @@ library LibRankify { * - The contract must be initialized. */ function enforceIsInitialized() internal view { - IRankifyInstanceCommons.RInstanceSettings storage settings = RInstanceStorage(); + InstanceState storage settings = instanceState(); require(settings.contractInitialized, "onlyInitialized"); } @@ -81,7 +156,30 @@ library LibRankify { */ function enforceGameExists(uint256 gameId) internal view { enforceIsInitialized(); - require(gameId.gameExists(), "no game found"); + require(gameId.gameExists(), "game not found"); + } + + struct NewGameParams { + uint256 gameId; + uint256 gameRank; + address creator; + uint256 minPlayerCnt; + uint256 maxPlayerCnt; + uint256 voteCredits; + address gameMaster; + uint96 nTurns; + uint128 minGameTime; + uint128 timePerTurn; + uint128 timeToJoin; + } + + function getGamePrice(uint128 minGameTime, CommonParams memory commonParams) internal pure returns (uint256) { + return + Math.mulDiv( + uint256(commonParams.principalCost), + uint256(commonParams.principalTimeConstant), + uint256(minGameTime) + ); } /** @@ -103,24 +201,63 @@ library LibRankify { * - Sets the rank of the game to `gameRank`. * - Mints new rank tokens. */ - function newGame(uint256 gameId, address gameMaster, uint256 gameRank, address creator) internal { - LibRankify.enforceIsInitialized(); - IRankifyInstanceCommons.RInstanceSettings storage settings = RInstanceStorage(); - gameId.createGame(gameMaster); // This will enforce game does not exist yet - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); - require(gameRank != 0, "game rank not specified"); - if (settings.gamePrice != 0) { - IERC20(settings.gamePaymentToken).transferFrom(creator, address(this), settings.gamePrice); - game.paymentsBalance = settings.gamePrice; - } + function newGame(NewGameParams memory params) internal { + enforceIsInitialized(); + CommonParams storage commonParams = instanceState().commonParams; - game.createdBy = creator; - settings.numGames += 1; - game.rank = gameRank; + require( + commonParams.principalTimeConstant % params.nTurns == 0, + IRankifyInstance.NoDivisionReminderAllowed(commonParams.principalTimeConstant, params.nTurns) + ); + require( + commonParams.principalTimeConstant % params.nTurns == 0, + IRankifyInstance.NoDivisionReminderAllowed(commonParams.principalTimeConstant, params.minGameTime) + ); + require( + params.minGameTime % params.nTurns == 0, + IRankifyInstance.NoDivisionReminderAllowed(params.nTurns, params.minGameTime) + ); + require(params.minGameTime > 0, "LibRankify::newGame->Min game time zero"); + require(params.nTurns > 2, IRankifyInstance.invalidTurnCount(params.nTurns)); - IRankToken rankTokenContract = IRankToken(settings.rankTokenAddress); - rankTokenContract.mint(address(this), 1, gameRank + 1, ""); - rankTokenContract.mint(address(this), 3, gameRank, ""); + LibTBG.Settings memory newSettings = LibTBG.Settings({ + timePerTurn: params.timePerTurn, + maxPlayerCnt: params.maxPlayerCnt, + minPlayerCnt: params.minPlayerCnt, + timeToJoin: params.timeToJoin, + maxTurns: params.nTurns, + voteCredits: params.voteCredits, + gameMaster: params.gameMaster, + implementationStoragePointer: bytes32(0) + }); + + InstanceState storage state = instanceState(); + + params.gameId.createGame(newSettings); // This will enforce game does not exist yet + GameState storage game = getGameState(params.gameId); + game.voting = LibQuadraticVoting.precomputeValues(params.voteCredits, params.maxPlayerCnt); + require( + SignedMath.abs(int256(uint256(params.minGameTime)) - int256(uint256(commonParams.principalTimeConstant))) < + uint256(commonParams.principalTimeConstant) * 16, + "Min game time out of bounds" + ); + uint256 principalGamePrice = getGamePrice(params.minGameTime, commonParams); + uint256 burnAmount = Math.mulDiv(principalGamePrice, 9, 10); + uint256 daoAmount = principalGamePrice - burnAmount; + address beneficiary = commonParams.beneficiary; + + Rankify(commonParams.gamePaymentToken).burnFrom(params.creator, burnAmount); + Rankify(commonParams.gamePaymentToken).transferFrom(params.creator, beneficiary, daoAmount); + + require(params.gameRank != 0, IRankifyInstance.RankNotSpecified()); + + game.createdBy = params.creator; + state.numGames += 1; + game.rank = params.gameRank; + game.minGameTime = params.minGameTime; + + IRankToken rankTokenContract = IRankToken(state.commonParams.rankTokenAddress); + rankTokenContract.mint(address(this), 1, params.gameRank + 1, ""); } /** @@ -133,7 +270,7 @@ library LibRankify { */ function enforceIsGameCreator(uint256 gameId, address candidate) internal view { enforceGameExists(gameId); - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); + GameState storage game = getGameState(gameId); require(game.createdBy == candidate, "Only game creator"); } @@ -183,12 +320,6 @@ library LibRankify { function joinGame(uint256 gameId, address player) internal { enforceGameExists(gameId); fulfillRankRq(gameId, player); - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - if (_RInstance.joinGamePrice != 0) { - IERC20(_RInstance.gamePaymentToken).transferFrom(player, address(this), _RInstance.joinGamePrice); - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); - game.paymentsBalance += _RInstance.joinGamePrice; - } gameId.addPlayer(player); } @@ -212,22 +343,25 @@ library LibRankify { */ function closeGame( uint256 gameId, - address beneficiary, function(uint256, address) playersGameEndedCallback ) internal returns (uint256[] memory) { enforceGameExists(gameId); - emitRankRewards(gameId, gameId.getLeaderBoard()); + + // Get game state and check minimum time + GameState storage game = getGameState(gameId); + LibTBG.State storage tbgState = gameId._getState(); + require( + block.timestamp - tbgState.startedAt >= game.minGameTime, + "Game duration less than minimum required time" + ); + (, uint256[] memory finalScores) = gameId.getScores(); address[] memory players = gameId.getPlayers(); for (uint256 i = 0; i < players.length; ++i) { removeAndUnlockPlayer(gameId, players[i]); playersGameEndedCallback(gameId, players[i]); } - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = LibRankify.RInstanceStorage(); - IERC20(_RInstance.gamePaymentToken).transfer( - beneficiary, - (_RInstance.joinGamePrice * players.length) + _RInstance.gamePrice - ); + emitRankRewards(gameId, gameId.getLeaderBoard()); return finalScores; } @@ -244,20 +378,7 @@ library LibRankify { * - Removes and unlocks `player` from the game. * - Calls `onPlayerLeftCallback` for `player`. */ - function quitGame( - uint256 gameId, - address player, - bool slash, - function(uint256, address) onPlayerLeftCallback - ) internal { - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - if (_RInstance.joinGamePrice != 0) { - uint256 divideBy = slash ? 2 : 1; - uint256 paymentRefund = _RInstance.joinGamePrice / divideBy; - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); - game.paymentsBalance -= paymentRefund; - IERC20(_RInstance.gamePaymentToken).transfer(player, paymentRefund); - } + function quitGame(uint256 gameId, address player, function(uint256, address) onPlayerLeftCallback) internal { removeAndUnlockPlayer(gameId, player); // this will throw if game has started or doesnt exist onPlayerLeftCallback(gameId, player); } @@ -276,28 +397,13 @@ library LibRankify { * - Decreases the game's payments balance by the refund amount. * - Transfers the remaining balance of the game to `beneficiary`. * - Deletes the game. - */ function cancelGame( - uint256 gameId, - function(uint256, address) onPlayerLeftCallback, - address beneficiary - ) internal { + */ function cancelGame(uint256 gameId, function(uint256, address) onPlayerLeftCallback) internal { // Cancel the game for each player address[] memory players = gameId.getPlayers(); for (uint256 i = 0; i < players.length; ++i) { - quitGame(gameId, players[i], false, onPlayerLeftCallback); //this will throw if game has started or doesnt exist + quitGame(gameId, players[i], onPlayerLeftCallback); //this will throw if game has started or doesnt exist } - // Refund payment to the game creator - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); - IRankifyInstanceCommons.RInstanceSettings storage _RInstance = RInstanceStorage(); - uint256 paymentRefund = _RInstance.gamePrice / 2; - IERC20(_RInstance.gamePaymentToken).transfer(game.createdBy, paymentRefund); - game.paymentsBalance -= paymentRefund; - - // Transfer remaining payments balance to the beneficiary - IERC20(_RInstance.gamePaymentToken).transfer(beneficiary, game.paymentsBalance); - game.paymentsBalance = 0; - // Delete the game gameId.deleteGame(); } @@ -308,16 +414,12 @@ library LibRankify { * Modifies: * * - Locks the rank token(s) of `player` in the rank token contract. - * - If the game has additional ranks, locks the additional ranks of `player` in the respective rank token contracts. */ function fulfillRankRq(uint256 gameId, address player) internal { - IRankifyInstanceCommons.RInstanceSettings storage settings = RInstanceStorage(); - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); + InstanceState storage instance = instanceState(); + GameState storage game = getGameState(gameId); if (game.rank > 1) { - _fulfillRankRq(player, game.rank, settings.rankTokenAddress); - for (uint256 i = 0; i < game.additionalRanks.length; ++i) { - _fulfillRankRq(player, game.rank, game.additionalRanks[i]); - } + _fulfillRankRq(player, game.rank, instance.commonParams.rankTokenAddress); } } @@ -329,11 +431,12 @@ library LibRankify { * - Transfers rank tokens from this contract to the top three addresses in the leaderboard. */ function emitRankReward(uint256 gameId, address[] memory leaderboard, address rankTokenAddress) private { - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); + GameState storage game = getGameState(gameId); IRankToken rankTokenContract = IRankToken(rankTokenAddress); + if (game.rank > 1) { + rankTokenContract.burn(leaderboard[0], game.rank, 1); + } rankTokenContract.safeTransferFrom(address(this), leaderboard[0], game.rank + 1, 1, ""); - rankTokenContract.safeTransferFrom(address(this), leaderboard[1], game.rank, 2, ""); - rankTokenContract.safeTransferFrom(address(this), leaderboard[2], game.rank, 1, ""); } /** @@ -344,12 +447,8 @@ library LibRankify { * - Calls `emitRankReward` for the main rank and each additional rank in the game. */ function emitRankRewards(uint256 gameId, address[] memory leaderboard) internal { - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); - IRankifyInstanceCommons.RInstanceSettings storage settings = LibRankify.RInstanceStorage(); - emitRankReward(gameId, leaderboard, settings.rankTokenAddress); - for (uint256 i = 0; i < game.additionalRanks.length; ++i) { - emitRankReward(gameId, leaderboard, game.additionalRanks[i]); - } + InstanceState storage instance = LibRankify.instanceState(); + emitRankReward(gameId, leaderboard, instance.commonParams.rankTokenAddress); } /** @@ -374,18 +473,15 @@ library LibRankify { * Modifies: * * - Removes `player` from the game. - * - If the game rank is greater than 1, unlocks the game rank token for `player` in the rank token contract and unlocks each additional rank token for `player` in the respective rank token contracts. + * - If the game rank is greater than 1, unlocks the game rank token for `player` in the rank token contract. */ function removeAndUnlockPlayer(uint256 gameId, address player) internal { enforceGameExists(gameId); gameId.removePlayer(player); //This will throw if game is in the process - IRankifyInstanceCommons.RInstanceSettings storage settings = RInstanceStorage(); - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); + InstanceState storage instance = instanceState(); + GameState storage game = getGameState(gameId); if (game.rank > 1) { - _releaseRankToken(player, game.rank, settings.rankTokenAddress); - for (uint256 i = 0; i < game.additionalRanks.length; ++i) { - _releaseRankToken(player, game.rank, game.additionalRanks[i]); - } + _releaseRankToken(player, game.rank, instance.commonParams.rankTokenAddress); } } @@ -404,13 +500,12 @@ library LibRankify { */ function tryPlayerMove(uint256 gameId, address player) internal returns (bool) { uint256 turn = gameId.getTurn(); - IRankifyInstanceCommons.RInstanceSettings storage settings = RInstanceStorage(); - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); + GameState storage game = getGameState(gameId); bool expectVote = true; bool expectProposal = true; if (turn == 1) expectVote = false; //Dont expect votes at firt turn // else if (gameId.isLastTurn()) expectProposal = false; // For now easiest solution is to keep collecting proposals as that is less complicated boundry case - if (game.numPrevProposals < settings.voting.minQuadraticPositons) expectVote = false; // If there is not enough proposals then round is skipped votes cannot be filled + if (game.numPrevProposals < game.voting.minQuadraticPositions) expectVote = false; // If there is not enough proposals then round is skipped votes cannot be filled bool madeMove = true; if (expectVote && !game.playerVoted[player]) madeMove = false; if (expectProposal && game.proposalCommitmentHashes[player] == "") madeMove = false; @@ -419,7 +514,7 @@ library LibRankify { } /** - * @dev Calculates the scores using a quadratic formula based on the revealed votes and proposer indices. `gameId` is the ID of the game. `votesRevealed` is an array of revealed votes. `proposerIndicies` is an array of proposer indices that links proposals to index in getPlayers(). + * @dev Calculates the scores using a quadratic formula based on the revealed votes and proposer indices. `gameId` is the ID of the game. `votesRevealed` is an array of revealed votes. `proposerIndices` is an array of proposer indices that links proposals to index in getPlayers(). * * Returns: * @@ -429,26 +524,25 @@ library LibRankify { function calculateScoresQuadratic( uint256 gameId, uint256[][] memory votesRevealed, - uint256[] memory proposerIndicies + uint256[] memory proposerIndices ) internal returns (uint256[] memory, uint256[] memory) { address[] memory players = gameId.getPlayers(); uint256[] memory scores = new uint256[](players.length); bool[] memory playerVoted = new bool[](players.length); - IRankifyInstanceCommons.RInstanceSettings storage settings = RInstanceStorage(); - IRankifyInstanceCommons.RInstance storage game = getGameStorage(gameId); + GameState storage game = getGameState(gameId); // Convert mappiing to array to pass it to libQuadratic for (uint256 i = 0; i < players.length; ++i) { playerVoted[i] = game.playerVoted[players[i]]; } - uint256[] memory roundScores = settings.voting.computeScoresByVPIndex( + uint256[] memory roundScores = game.voting.computeScoresByVPIndex( votesRevealed, playerVoted, - settings.voting.maxQuadraticPoints, - proposerIndicies.length + game.voting.maxQuadraticPoints, + proposerIndices.length ); for (uint256 playerIdx = 0; playerIdx < players.length; playerIdx++) { //for each player - if (proposerIndicies[playerIdx] < players.length) { + if (proposerIndices[playerIdx] < players.length) { //if player proposal exists scores[playerIdx] = gameId.getScore(players[playerIdx]) + roundScores[playerIdx]; gameId.setScore(players[playerIdx], scores[playerIdx]); diff --git a/src/libraries/LibReentrancyGuard.sol b/src/libraries/LibReentrancyGuard.sol index bad1e31c..130a9448 100644 --- a/src/libraries/LibReentrancyGuard.sol +++ b/src/libraries/LibReentrancyGuard.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -// import "./LibDiamondOwner.sol"; -// import { IMultipass } from "../interfaces/sol"; import "hardhat/console.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; diff --git a/src/libraries/LibTurnBasedGame.sol b/src/libraries/LibTurnBasedGame.sol index 9ad93521..b2e33fea 100644 --- a/src/libraries/LibTurnBasedGame.sol +++ b/src/libraries/LibTurnBasedGame.sol @@ -9,7 +9,7 @@ import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {LibArray} from "../libraries/LibArray.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; - +import {IErrors} from "../interfaces/IErrors.sol"; /** * @title LibTBG * @dev Library for managing turn-based games. @@ -28,41 +28,45 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; library LibTBG { using EnumerableSet for EnumerableSet.AddressSet; - struct GameSettings { + struct Settings { uint256 timePerTurn; - uint256 maxPlayersSize; - uint256 minPlayersSize; + uint256 maxPlayerCnt; + uint256 minPlayerCnt; uint256 timeToJoin; uint256 maxTurns; - uint256 numWinners; uint256 voteCredits; + address gameMaster; + bytes32 implementationStoragePointer; } - struct GameInstance { - address gameMaster; + struct State { uint256 currentTurn; uint256 turnStartedAt; uint256 registrationOpenAt; + uint256 startedAt; bool hasStarted; bool hasEnded; EnumerableSet.AddressSet players; mapping(address => bool) madeMove; uint256 numPlayersMadeMove; mapping(address => uint256) score; - bytes32 implemenationStoragePointer; bool isOvertime; address[] leaderboard; } + struct Instance { + Settings settings; + State state; + } + struct TBGStorageStruct { - GameSettings settings; - mapping(uint256 => GameInstance) games; + mapping(uint256 => Instance) instances; mapping(address => uint256) playerInGame; uint256 totalGamesCreated; } - bytes32 constant TBG_STORAGE_POSITION = keccak256("turnbasedgame.storage.position"); - bytes32 constant IMPLEMENTATION_STORAGE_POSITION = keccak256("implementation.turnbasedgame.storage.position"); + bytes32 constant TBG_STORAGE_POSITION = keccak256("turn_based_game.storage.position"); + bytes32 constant IMPLEMENTATION_STORAGE_POSITION = keccak256("implementation.turn_based_game.storage.position"); function TBGStorage() internal pure returns (TBGStorageStruct storage es) { bytes32 position = TBG_STORAGE_POSITION; @@ -71,9 +75,14 @@ library LibTBG { } } - function _getGame(uint256 gameId) internal view returns (GameInstance storage) { + function _getInstance(uint256 gameId) internal view returns (Instance storage) { TBGStorageStruct storage tbg = TBGStorage(); - return tbg.games[gameId]; + return tbg.instances[gameId]; + } + + function _getState(uint256 gameId) internal view returns (State storage) { + TBGStorageStruct storage tbg = TBGStorage(); + return tbg.instances[gameId].state; } /** @@ -82,27 +91,30 @@ library LibTBG { * Requirements: * * - `settings.timePerTurn` must not be zero. - * - `settings.maxPlayersSize` must not be zero. - * - `settings.minPlayersSize` must be at least 2. + * - `settings.maxPlayerCnt` must not be zero. + * - `settings.minPlayerCnt` must be at least 2. * - `settings.maxTurns` must not be zero. - * - `settings.numWinners` must not be zero and must be less than `settings.minPlayersSize`. * - `settings.timeToJoin` must not be zero. - * - `settings.maxPlayersSize` must not be less than `settings.minPlayersSize`. + * - `settings.maxPlayerCnt` must not be less than `settings.minPlayerCnt`. * Modifies: * * - Sets the settings of the game to `settings`. */ - function init(GameSettings memory settings) internal { - TBGStorageStruct storage tbg = TBGStorage(); - if (settings.timePerTurn == 0) require(false, "settings.timePerTurn"); // revert invalidConfiguration('timePerTurn'); - if (settings.maxPlayersSize == 0) require(false, "settings.maxPlayersSize"); // revert invalidConfiguration('maxPlayersSize'); - if (settings.minPlayersSize < 2) require(false, "settings.minPlayersSize"); //revert invalidConfiguration('minPlayersSize'); - if (settings.maxTurns == 0) require(false, "settings.maxTurns"); //revert invalidConfiguration('maxTurns'); - if (settings.numWinners == 0 || settings.numWinners >= settings.minPlayersSize) require(false, "numWinners"); //revert invalidConfiguration('numWinners'); - if (settings.timeToJoin == 0) require(false, "timeToJoin"); // revert invalidConfiguration('timeToJoin'); - if (settings.maxPlayersSize < settings.minPlayersSize) require(false, "maxPlayersSize"); //revert invalidConfiguration('maxPlayersSize'); - tbg.settings = settings; + function init(uint256 gameId, Settings memory newSettings) private { + TBGStorageStruct storage tbg = TBGStorage(); + Settings storage settings = tbg.instances[gameId].settings; + require(newSettings.timePerTurn != 0, IErrors.invalidConfiguration("LibTBG::init->settings.timePerTurn")); + require(newSettings.maxPlayerCnt != 0, IErrors.invalidConfiguration("LibTBG::init->settings.maxPlayerCnt")); + require(newSettings.minPlayerCnt > 1, IErrors.invalidConfiguration("LibTBG::init->settings.minPlayerCnt")); + require(newSettings.maxTurns != 0, IErrors.invalidConfiguration("LibTBG::init->settings.maxTurns")); + require(newSettings.timeToJoin != 0, IErrors.invalidConfiguration("LibTBG::init->timeToJoin")); + require( + settings.minPlayerCnt < newSettings.maxPlayerCnt, + IErrors.invalidConfiguration("LibTBG::init->maxPlayerCnt") + ); + require(newSettings.gameMaster != address(0), IErrors.invalidConfiguration("LibTBG::init->gameMaster")); + tbg.instances[gameId].settings = newSettings; } /** @@ -120,18 +132,16 @@ library LibTBG { * - Sets the game master of the game with `gameId` to `gm`. * - Increments the total number of games created. */ - function createGame(uint256 gameId, address gm) internal { - TBGStorageStruct storage tbg = TBGStorage(); + function createGame(uint256 gameId, Settings memory settings) internal { require(!gameExists(gameId), "createGame->Already exists"); - require(gm != address(0), "createGame->GM"); require(gameId != 0, "createGame->gameId"); - require(tbg.games[gameId].gameMaster == address(0), "createGame->gameId"); - tbg.games[gameId].gameMaster = gm; + init(gameId, settings); + TBGStorageStruct storage tbg = TBGStorage(); tbg.totalGamesCreated += 1; - //totalGamesCreated ensures nonce-like behaviur: + //totalGamesCreated ensures nonce-like behavior: //even if game would get deleted and re-created with same name, data storage would be different - tbg.games[gameId].implemenationStoragePointer = keccak256( + tbg.instances[gameId].settings.implementationStoragePointer = keccak256( abi.encode(gameId, tbg.totalGamesCreated, TBG_STORAGE_POSITION) ); } @@ -154,23 +164,20 @@ library LibTBG { */ function deleteGame(uint256 gameId) internal { TBGStorageStruct storage tbg = TBGStorage(); - GameInstance storage _game = _getGame(gameId); - address[] memory players = _game.players.values(); + address[] memory players = tbg.instances[gameId].state.players.values(); for (uint256 i = 0; i < players.length; ++i) { - tbg.games[gameId].score[players[i]] = 0; - tbg.games[gameId].madeMove[players[i]] = false; + tbg.instances[gameId].state.score[players[i]] = 0; + tbg.instances[gameId].state.madeMove[players[i]] = false; } - delete tbg.games[gameId].gameMaster; - delete tbg.games[gameId].currentTurn; - delete tbg.games[gameId].hasEnded; - delete tbg.games[gameId].hasStarted; - delete tbg.games[gameId].implemenationStoragePointer; - delete tbg.games[gameId].isOvertime; - delete tbg.games[gameId].leaderboard; - delete tbg.games[gameId].numPlayersMadeMove; - delete tbg.games[gameId].players; - delete tbg.games[gameId].registrationOpenAt; - delete tbg.games[gameId].turnStartedAt; + delete tbg.instances[gameId].state.currentTurn; + delete tbg.instances[gameId].state.hasEnded; + delete tbg.instances[gameId].state.hasStarted; + delete tbg.instances[gameId].state.isOvertime; + delete tbg.instances[gameId].state.leaderboard; + delete tbg.instances[gameId].state.numPlayersMadeMove; + delete tbg.instances[gameId].state.players; + delete tbg.instances[gameId].state.registrationOpenAt; + delete tbg.instances[gameId].state.turnStartedAt; } /** @@ -181,8 +188,8 @@ library LibTBG { * - A boolean indicating whether the game can be joined. */ function canBeJoined(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); - if (_game.hasStarted || _game.registrationOpenAt == 0) return false; + State storage state = _getState(gameId); + if (state.hasStarted || state.registrationOpenAt == 0) return false; return true; } @@ -204,15 +211,16 @@ library LibTBG { */ function addPlayer(uint256 gameId, address participant) internal { TBGStorageStruct storage tbg = TBGStorage(); + State storage state = tbg.instances[gameId].state; + Settings storage settings = tbg.instances[gameId].settings; require(gameExists(gameId), "addPlayer->invalid game"); require(tbg.playerInGame[participant] == 0, "addPlayer->Player in game"); - GameInstance storage _game = _getGame(gameId); - require(_game.players.length() < tbg.settings.maxPlayersSize, "addPlayer->party full"); + require(state.players.length() < settings.maxPlayerCnt, "addPlayer->party full"); require(canBeJoined(gameId), "addPlayer->cant join now"); - _game.players.add(participant); - _game.madeMove[participant] = false; + state.players.add(participant); + state.madeMove[participant] = false; tbg.playerInGame[participant] = gameId; } @@ -244,12 +252,12 @@ library LibTBG { */ function removePlayer(uint256 gameId, address participant) internal { TBGStorageStruct storage tbg = TBGStorage(); - GameInstance storage _game = _getGame(gameId); + State storage state = tbg.instances[gameId].state; require(gameExists(gameId), "game does not exist"); require(tbg.playerInGame[participant] == gameId, "Not in the game"); - require(_game.hasStarted == false || _game.hasEnded == true, "Cannot leave once started"); + require(state.hasStarted == false || state.hasEnded == true, "Cannot leave once started"); tbg.playerInGame[participant] = 0; - _game.players.remove(participant); + state.players.remove(participant); } /** @@ -266,10 +274,10 @@ library LibTBG { */ function isTurnTimedOut(uint256 gameId) internal view returns (bool) { TBGStorageStruct storage tbg = TBGStorage(); - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); assert(gameId != 0); - assert(_game.hasStarted == true); - if (block.timestamp <= tbg.settings.timePerTurn + _game.turnStartedAt) return false; + assert(state.hasStarted == true); + if (block.timestamp <= tbg.instances[gameId].settings.timePerTurn + state.turnStartedAt) return false; return true; } @@ -281,8 +289,8 @@ library LibTBG { * - A boolean indicating whether the game exists. */ function gameExists(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); - if (_game.gameMaster != address(0)) return true; + Settings storage settings = getSettings(gameId); + if (settings.gameMaster != address(0)) return true; return false; } @@ -295,9 +303,9 @@ library LibTBG { * - The game with `gameId` must have started. */ function enforceHasStarted(uint256 gameId) internal view { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); assert(gameId != 0); - require(_game.hasStarted, "Game has not yet started"); + require(state.hasStarted, "Game has not yet started"); } /** @@ -311,8 +319,8 @@ library LibTBG { */ function canEndTurn(uint256 gameId) internal view returns (bool) { bool turnTimedOut = isTurnTimedOut(gameId); - GameInstance storage _game = _getGame(gameId); - if (!_game.hasStarted || isGameOver(gameId)) return false; + State storage state = _getState(gameId); + if (!state.hasStarted || isGameOver(gameId)) return false; if (turnTimedOut) return true; return false; } @@ -325,9 +333,9 @@ library LibTBG { * - A boolean indicating whether the current turn can end early. */ function canEndTurnEarly(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); - bool everyoneMadeMove = (_game.numPlayersMadeMove) == _game.players.length() ? true : false; - if (!_game.hasStarted || isGameOver(gameId)) return false; + State storage state = _getState(gameId); + bool everyoneMadeMove = (state.numPlayersMadeMove) == state.players.length() ? true : false; + if (!state.hasStarted || isGameOver(gameId)) return false; if (everyoneMadeMove || canEndTurn(gameId)) return true; return false; } @@ -350,32 +358,32 @@ library LibTBG { } /** - * @dev Clears the current moves in a game. `game` is the game. + * @dev Clears the current moves in a game. `state` is the State. * * Modifies: * * - Sets the madeMove of each player in `game` to false. */ - function _clearCurrentMoves(GameInstance storage game) internal { - for (uint256 i = 0; i < game.players.length(); ++i) { - address player = game.players.at(i); - game.madeMove[player] = false; + function _clearCurrentMoves(State storage state) internal { + for (uint256 i = 0; i < state.players.length(); ++i) { + address player = state.players.at(i); + state.madeMove[player] = false; } - game.numPlayersMadeMove = 0; + state.numPlayersMadeMove = 0; } /** - * @dev Resets the states of the players in a game. `game` is the game. + * @dev Resets the states of the players in a game. `State` is the state. * * Modifies: * * - Sets the madeMove and score of each player in `game` to their initial values. */ - function _resetPlayerStates(GameInstance storage game) internal { - for (uint256 i = 0; i < game.players.length(); ++i) { - address player = game.players.at(i); - game.madeMove[player] = false; - game.score[player] = 0; + function _resetPlayerStates(State storage state) internal { + for (uint256 i = 0; i < state.players.length(); ++i) { + address player = state.players.at(i); + state.madeMove[player] = false; + state.score[player] = 0; } } @@ -391,9 +399,9 @@ library LibTBG { * - Sets the score of `player` in the game with `gameId` to `value`. */ function setScore(uint256 gameId, address player, uint256 value) internal { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); require(isPlayerInGame(gameId, player), "player not in a game"); - _game.score[player] = value; + state.score[player] = value; } /** @@ -404,8 +412,8 @@ library LibTBG { * - The score of `player` in the game with `gameId`. */ function getScore(uint256 gameId, address player) internal view returns (uint256) { - GameInstance storage _game = _getGame(gameId); - return _game.score[player]; + State storage state = _getState(gameId); + return state.score[player]; } /** @@ -438,8 +446,8 @@ library LibTBG { */ function openRegistration(uint256 gameId) internal { require(gameExists(gameId), "game not found"); - GameInstance storage _game = _getGame(gameId); - _game.registrationOpenAt = block.timestamp; + State storage state = _getState(gameId); + state.registrationOpenAt = block.timestamp; } /** @@ -450,12 +458,13 @@ library LibTBG { * - A boolean indicating whether registration is open for the game. */ function isRegistrationOpen(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); TBGStorageStruct storage tbg = TBGStorage(); - if (_game.registrationOpenAt == 0) { + if (state.registrationOpenAt == 0) { return false; } else { - return _game.registrationOpenAt < block.timestamp + tbg.settings.timeToJoin ? true : false; + return + state.registrationOpenAt < block.timestamp + tbg.instances[gameId].settings.timeToJoin ? true : false; } } @@ -467,13 +476,13 @@ library LibTBG { * - A boolean indicating whether the game can start. */ function canStart(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); TBGStorageStruct storage tbg = TBGStorage(); - if (_game.hasStarted) return false; - if (_game.registrationOpenAt == 0) return false; + if (state.hasStarted) return false; + if (state.registrationOpenAt == 0) return false; if (gameId == 0) return false; - if (block.timestamp <= _game.registrationOpenAt + tbg.settings.timeToJoin) return false; - if (_game.players.length() < tbg.settings.minPlayersSize) return false; + if (block.timestamp <= state.registrationOpenAt + tbg.instances[gameId].settings.timeToJoin) return false; + if (state.players.length() < tbg.instances[gameId].settings.minPlayerCnt) return false; return true; } @@ -486,13 +495,33 @@ library LibTBG { * - A boolean indicating whether the game can start early. */ function canStartEarly(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); TBGStorageStruct storage tbg = TBGStorage(); - if ((_game.players.length() == tbg.settings.maxPlayersSize) || canStart(gameId)) return true; + if ((state.players.length() == tbg.instances[gameId].settings.maxPlayerCnt) || canStart(gameId)) return true; return false; } + /** + * @dev Internal function to perform common game start operations + * @param gameId The ID of the game to start + * @param state The game state storage reference + * @param tbg The TBG storage reference + */ + function _performGameStart(uint256 gameId, State storage state, TBGStorageStruct storage tbg) private { + require(state.hasStarted == false, "startGame->already started"); + require(state.registrationOpenAt != 0, "startGame->Game registration was not yet open"); + require(gameId != 0, "startGame->Game not found"); + require(state.players.length() >= tbg.instances[gameId].settings.minPlayerCnt, "startGame->Not enough players"); + + state.hasStarted = true; + state.hasEnded = false; + state.currentTurn = 1; + state.turnStartedAt = block.timestamp; + state.startedAt = block.timestamp; + _resetPlayerStates(state); + } + /** * @dev Starts a game with the provided game ID early. `gameId` is the ID of the game. * By "early" it is assumed that time to join has not yet passed, but it's already cap players limit reached. @@ -511,22 +540,16 @@ library LibTBG { * - Resets the states of the players in the game with `gameId`. */ function startGameEarly(uint256 gameId) internal { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); TBGStorageStruct storage tbg = TBGStorage(); - require(_game.hasStarted == false, "startGame->already started"); - require(_game.registrationOpenAt != 0, "startGame->Game registration was not yet open"); - require(gameId != 0, "startGame->Game not found"); - require(_game.players.length() >= tbg.settings.minPlayersSize, "startGame->Not enough players"); + require( - (_game.players.length() == tbg.settings.maxPlayersSize) || - (block.timestamp > _game.registrationOpenAt + tbg.settings.timeToJoin), + (state.players.length() == tbg.instances[gameId].settings.maxPlayerCnt) || + (block.timestamp > state.registrationOpenAt + tbg.instances[gameId].settings.timeToJoin), "startGame->Not enough players" ); - _game.hasStarted = true; - _game.hasEnded = false; - _game.currentTurn = 1; - _game.turnStartedAt = block.timestamp; - _resetPlayerStates(_game); + + _performGameStart(gameId, state, tbg); } /** @@ -545,18 +568,15 @@ library LibTBG { * - Resets the states of the players in the game with `gameId`. */ function startGame(uint256 gameId) internal { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); TBGStorageStruct storage tbg = TBGStorage(); - require(_game.hasStarted == false, "startGame->already started"); - require(_game.registrationOpenAt != 0, "startGame->Game registration was not yet open"); - require(block.timestamp > _game.registrationOpenAt + tbg.settings.timeToJoin, "startGame->Still Can Join"); - require(gameId != 0, "startGame->Game not found"); - require(_game.players.length() >= tbg.settings.minPlayersSize, "startGame->Not enough players"); - _game.hasStarted = true; - _game.hasEnded = false; - _game.currentTurn = 1; - _game.turnStartedAt = block.timestamp; - _resetPlayerStates(_game); + + require( + block.timestamp > state.registrationOpenAt + tbg.instances[gameId].settings.timeToJoin, + "startGame->Still Can Join" + ); + + _performGameStart(gameId, state, tbg); } /** @@ -567,8 +587,8 @@ library LibTBG { * - The current turn of the game with `gameId`. */ function getTurn(uint256 gameId) internal view returns (uint256) { - GameInstance storage _game = _getGame(gameId); - return _game.currentTurn; + State storage state = _getState(gameId); + return state.currentTurn; } /** @@ -579,8 +599,8 @@ library LibTBG { * - The game master of the game with `gameId`. */ function getGM(uint256 gameId) internal view returns (address) { - GameInstance storage _game = _getGame(gameId); - return _game.gameMaster; + Settings storage settings = getSettings(gameId); + return settings.gameMaster; } /** @@ -592,8 +612,8 @@ library LibTBG { */ function isLastTurn(uint256 gameId) internal view returns (bool) { TBGStorageStruct storage tbg = TBGStorage(); - GameInstance storage _game = _getGame(gameId); - if (_game.currentTurn == tbg.settings.maxTurns) return true; + State storage state = _getState(gameId); + if (state.currentTurn == tbg.instances[gameId].settings.maxTurns) return true; else return false; } @@ -606,8 +626,8 @@ library LibTBG { */ function isGameOver(uint256 gameId) internal view returns (bool) { TBGStorageStruct storage tbg = TBGStorage(); - GameInstance storage _game = _getGame(gameId); - if ((_game.currentTurn > tbg.settings.maxTurns) && !_game.isOvertime) return true; + State storage state = _getState(gameId); + if ((state.currentTurn > tbg.instances[gameId].settings.maxTurns) && !state.isOvertime) return true; else return false; } @@ -638,19 +658,19 @@ library LibTBG { * - Increments the numPlayersMadeMove of the game with `gameId`. */ function playerMove(uint256 gameId, address player) internal onlyInTurnTime(gameId) { - GameInstance storage _game = _getGame(gameId); + State storage state = _getState(gameId); enforceHasStarted(gameId); enforceIsNotOver(gameId); - require(_game.madeMove[player] == false, "already made a move"); + require(state.madeMove[player] == false, "already made a move"); TBGStorageStruct storage tbg = TBGStorage(); require(gameId == tbg.playerInGame[player], "is not in the game"); - _game.madeMove[player] = true; - _game.numPlayersMadeMove += 1; + state.madeMove[player] = true; + state.numPlayersMadeMove += 1; } function isPlayerTurnComplete(uint256 gameId, address player) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); - return _game.madeMove[player]; + State storage state = _getState(gameId); + return state.madeMove[player]; } /** @@ -673,8 +693,8 @@ library LibTBG { * - A boolean indicating whether the game has started. */ function hasStarted(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); - return _game.hasStarted; + State storage state = _getState(gameId); + return state.hasStarted; } /** @@ -685,8 +705,8 @@ library LibTBG { * - An array of the addresses of the players in the game with `gameId`, sorted by score. */ function getLeaderBoard(uint256 gameId) internal view returns (address[] memory) { - GameInstance storage _game = _getGame(gameId); - return _game.leaderboard; + State storage state = _getState(gameId); + return state.leaderboard; } /** @@ -712,19 +732,19 @@ library LibTBG { */ function nextTurn(uint256 gameId) internal returns (bool, bool, bool) { require(canEndTurnEarly(gameId), "nextTurn->CanEndEarly"); - GameInstance storage _game = _getGame(gameId); - _clearCurrentMoves(_game); - _game.currentTurn += 1; - _game.turnStartedAt = block.timestamp; + State storage state = _getState(gameId); + _clearCurrentMoves(state); + state.currentTurn += 1; + state.turnStartedAt = block.timestamp; bool _isLastTurn = isLastTurn(gameId); - if (_isLastTurn || _game.isOvertime) { + if (_isLastTurn || state.isOvertime) { bool _isTie = isTie(gameId); - _game.isOvertime = _isTie; + state.isOvertime = _isTie; } - _game.hasEnded = isGameOver(gameId); + state.hasEnded = isGameOver(gameId); - (_game.leaderboard, ) = sortByScore(gameId); - return (_isLastTurn, _game.isOvertime, _game.hasEnded); + (state.leaderboard, ) = sortByScore(gameId); + return (_isLastTurn, state.isOvertime, state.hasEnded); } /** @@ -746,8 +766,8 @@ library LibTBG { * - The game data storage pointer of the game with `gameId`. */ function getGameDataStorage(uint256 gameId) internal view returns (bytes32 pointer) { - GameInstance storage _game = _getGame(gameId); - return _game.implemenationStoragePointer; + Settings storage settings = getSettings(gameId); + return settings.implementationStoragePointer; } /** @@ -758,8 +778,8 @@ library LibTBG { * - The number of players in the game with `gameId`. */ function getPlayersNumber(uint256 gameId) internal view returns (uint256) { - GameInstance storage _game = _getGame(gameId); - return _game.players.length(); + State storage state = _getState(gameId); + return state.players.length(); } /** @@ -770,8 +790,8 @@ library LibTBG { * - An array of the addresses of the players in the game with `gameId`. */ function getPlayers(uint256 gameId) internal view returns (address[] memory) { - GameInstance storage _game = _getGame(gameId); - return _game.players.values(); + State storage state = _getState(gameId); + return state.players.values(); } /** @@ -781,9 +801,9 @@ library LibTBG { * * - The game settings. */ - function getGameSettings() internal view returns (GameSettings memory) { + function getSettings(uint256 gameId) internal view returns (Settings storage) { TBGStorageStruct storage tbg = TBGStorage(); - return tbg.settings; + return tbg.instances[gameId].settings; } /** @@ -807,8 +827,8 @@ library LibTBG { * - Sets the isOvertime of the game with `gameId` to true. */ function addOvertime(uint256 gameId) internal { - GameInstance storage _game = _getGame(gameId); - _game.isOvertime = true; + State storage state = _getState(gameId); + state.isOvertime = true; } /** @@ -819,8 +839,8 @@ library LibTBG { * - A boolean indicating whether the game is in overtime. */ function isOvertime(uint256 gameId) internal view returns (bool) { - GameInstance storage _game = _getGame(gameId); - return _game.isOvertime; + State storage state = _getState(gameId); + return state.isOvertime; } /** @@ -831,32 +851,27 @@ library LibTBG { * - Sets the isOvertime of the game with `gameId` to false. */ function resetOvertime(uint256 gameId) internal { - GameInstance storage _game = _getGame(gameId); - _game.isOvertime = false; + State storage state = _getState(gameId); + state.isOvertime = false; } /** * @dev Checks if a game with the provided game ID is a tie. `gameId` is the ID of the game. - * Tie being defined as at least two of the top `numWinners` players having the same score. + * Tie being defined as at least two of the top `numWinners=1` players having the same score. * * Returns: * * - A boolean indicating whether the game is a tie. */ function isTie(uint256 gameId) internal view returns (bool) { - TBGStorageStruct storage tbg = TBGStorage(); - (address[] memory players, uint256[] memory scores) = getScores(gameId); + (, uint256[] memory scores) = getScores(gameId); LibArray.quickSort(scores, int256(0), int256(scores.length - 1)); - for (uint256 i = 0; i < players.length - 1; ++i) { - if ((i <= tbg.settings.numWinners - 1)) { - if (scores[i] == scores[i + 1]) { - return (true); - } - } else { - break; - } + + if (scores[0] == scores[1]) { + return (true); } + return (false); } diff --git a/src/mocks/RankifyInstanceEventMock.sol b/src/mocks/RankifyInstanceEventMock.sol index 7f0eeaed..f02ba12d 100644 --- a/src/mocks/RankifyInstanceEventMock.sol +++ b/src/mocks/RankifyInstanceEventMock.sol @@ -17,7 +17,7 @@ contract RankifyInstanceEventMock { address[] players, uint256[] scores, string[] newProposals, - uint256[] proposerIndicies, + uint256[] proposerIndices, uint256[][] votes ); diff --git a/src/tokens/DistributableGovernanceERC20.sol b/src/tokens/DistributableGovernanceERC20.sol index 47789010..e37de059 100644 --- a/src/tokens/DistributableGovernanceERC20.sol +++ b/src/tokens/DistributableGovernanceERC20.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity 0.8.20; +pragma solidity =0.8.28; // import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; diff --git a/src/tokens/RankToken.sol b/src/tokens/RankToken.sol index a0cd15d8..43f2df3a 100644 --- a/src/tokens/RankToken.sol +++ b/src/tokens/RankToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity =0.8.28; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {IRankToken} from "../interfaces/IRankToken.sol"; @@ -7,6 +7,7 @@ import "../abstracts/LockableERC1155.sol"; import "@peeramid-labs/eds/src/abstracts/ERC7746Middleware.sol"; import "@peeramid-labs/eds/src/libraries/LibMiddleware.sol"; import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol"; +import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; //ToDo: it was planned to make it track for highest token users hold (their rank), right now it's not implemented. Yet. @@ -123,4 +124,12 @@ contract RankToken is LockableERC1155, IRankToken, ERC7746Middleware { ) public view virtual override(IERC165, ERC1155Upgradeable) returns (bool) { return interfaceId == type(IRankToken).interfaceId || super.supportsInterface(interfaceId); } + + function burn( + address account, + uint256 id, + uint256 value + ) public override(LockableERC1155, ILockableERC1155) ERC7746C(msg.sig, msg.sender, msg.data, 0) { + super.burn(account, id, value); + } } diff --git a/src/vendor/diamond/DiamondCloneable.sol b/src/vendor/diamond/DiamondClonable.sol similarity index 95% rename from src/vendor/diamond/DiamondCloneable.sol rename to src/vendor/diamond/DiamondClonable.sol index f10cc6a2..b5f586b2 100644 --- a/src/vendor/diamond/DiamondCloneable.sol +++ b/src/vendor/diamond/DiamondClonable.sol @@ -7,8 +7,8 @@ pragma solidity ^0.8.0; import {LibDiamond} from "./libraries/LibDiamond.sol"; import {IDiamondCut} from "./interfaces/IDiamondCut.sol"; -contract DiamondCloneable { - error fucntionDoesNotExist(bytes4 selector); +contract DiamondClonable { + error functionDoesNotExist(bytes4 selector); address private immutable cutFacet; constructor(address _contractOwner, address _diamondCutFacet) payable { @@ -54,7 +54,7 @@ contract DiamondCloneable { LibDiamond.diamondCut(facets, target, data); return; } else { - revert fucntionDoesNotExist(msg.sig); + revert functionDoesNotExist(msg.sig); } } diff --git a/src/vendor/diamond/facets/DiamondLoupeFacet.sol b/src/vendor/diamond/facets/DiamondLoupeFacet.sol index 2774f27d..bd719274 100644 --- a/src/vendor/diamond/facets/DiamondLoupeFacet.sol +++ b/src/vendor/diamond/facets/DiamondLoupeFacet.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; /******************************************************************************\ * Author: Nick Mudge (https://twitter.com/mudgen) diff --git a/src/vendor/diamond/facets/OwnershipFacet.sol b/src/vendor/diamond/facets/OwnershipFacet.sol index c5ec562e..9bd9202e 100644 --- a/src/vendor/diamond/facets/OwnershipFacet.sol +++ b/src/vendor/diamond/facets/OwnershipFacet.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import "../libraries/LibDiamond.sol"; import "../interfaces/IERC173.sol"; diff --git a/src/vendor/diamond/libraries/LibDiamond.sol b/src/vendor/diamond/libraries/LibDiamond.sol index 5e501ce5..765d6b98 100644 --- a/src/vendor/diamond/libraries/LibDiamond.sol +++ b/src/vendor/diamond/libraries/LibDiamond.sol @@ -77,7 +77,7 @@ library LibDiamond { emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } - + error DuplicateSignature(bytes4 _selector); function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); @@ -91,6 +91,7 @@ library LibDiamond { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); + addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } diff --git a/test/DNSFacet.ts b/test/DNSFacet.ts deleted file mode 100644 index 4a3238b2..00000000 --- a/test/DNSFacet.ts +++ /dev/null @@ -1,874 +0,0 @@ -import { AdrSetupResult, EnvSetupResult, SignerIdentity, setupTest } from './utils'; -import { setupAddresses, setupEnvironment, getUserRegisterProps, signRegistrarMessage } from './utils'; -import { getInterfaceID } from '../scripts/libraries/utils'; -import { expect } from 'chai'; -import { ethers } from 'hardhat'; -import { IMultipass__factory } from '../types'; -const path = require('path'); -import hre from 'hardhat'; -const forkBlockNumber = hre.config.networks.hardhat.forking?.blockNumber ?? 0; -import { LibMultipass } from '../types/src/facets/DNSFacet'; - -const scriptName = path.basename(__filename); -const NEW_DOMAIN_NAME1 = 'newDomainName1'; -const NEW_DOMAIN_NAME2 = 'newDomainName2'; -const DEFAULT_FREE_REGISTRATIONS = ethers.BigNumber.from(3); -const NOT_ENOUGH_FEE = ethers.utils.parseEther('0.17'); -const DEFAULT_FEE = ethers.utils.parseEther('2'); -const FEE_AFTER_CHANGE = ethers.utils.parseEther('3'); -const DEFAULT_DISCOUNT = ethers.utils.parseEther('1'); -const DEFAULT_REWARD = ethers.utils.parseEther('0.5'); -let adr: AdrSetupResult; - -let env: EnvSetupResult; - -const emptyUserQuery: LibMultipass.NameQueryStruct = { - name: ethers.utils.formatBytes32String(''), - id: ethers.utils.formatBytes32String(''), - domainName: ethers.utils.formatBytes32String(''), - wallet: ethers.constants.AddressZero, - targetDomain: ethers.utils.formatBytes32String(''), -}; - -describe(scriptName, () => { - beforeEach(async () => { - const setup = await setupTest(); - adr = setup.adr; - env = setup.env; - }); - it('Is Owned by contract owner', async () => { - expect(await env.multipass.owner()).to.be.equal(adr.multipassOwner.wallet.address); - }); - it('Transfer ownership can be done only by contract owner', async () => { - await expect( - env.multipass.connect(adr.multipassOwner.wallet).transferOwnership(adr.gameCreator1.wallet.address), - ).to.emit(env.multipass, 'OwnershipTransferred(address,address)'); - - await expect( - env.multipass.connect(adr.maliciousActor1.wallet).transferOwnership(adr.gameCreator1.wallet.address), - ).to.revertedWith('LibDiamond: Must be contract owner'); - }); - it('Has zero domains', async () => { - expect(await env.multipass.getContractState()).to.be.equal(0); - }); - it('Supports multipass interface', async () => { - const MultipassInterface = IMultipass__factory.createInterface(); - const multipassInterfaceId = getInterfaceID(MultipassInterface); - expect(await env.multipass.supportsInterface(multipassInterfaceId._hex)).to.be.true; - }); - it('Emits and increments when new domain initialized', async () => { - await expect( - await env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - 1000, - ethers.utils.parseEther('3'), - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - ethers.utils.parseEther('1'), - ethers.utils.parseEther('1'), - ), - ).to.emit(env.multipass, 'InitializedDomain'); - expect(await env.multipass.getContractState()).to.be.equal(1); - }); - it('Reverts domain specific methods if domain does not exists', async () => { - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .activateDomain(ethers.utils.formatBytes32String('invalidDomain')), - ).to.be.revertedWith('Domain does not exist'); - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .deactivateDomain(ethers.utils.formatBytes32String('invalidDomain')), - ).to.be.revertedWith('Domain does not exist'); - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .changeFee(ethers.utils.formatBytes32String('invalidDomain'), '1'), - ).to.be.revertedWith('Domain does not exist'); - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .changeRegistrar(ethers.utils.formatBytes32String('invalidDomain'), adr.gameCreator1.wallet.address), - ).to.be.revertedWith('Domain does not exist'); - const invalidDomainQuery = { ...emptyUserQuery }; - invalidDomainQuery.domainName = ethers.utils.formatBytes32String('invalidDomain'); - invalidDomainQuery.targetDomain = ethers.utils.formatBytes32String('invalidDomain'); - - const registrarMessage = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - deadline: ethers.BigNumber.from(forkBlockNumber + 9999), - nonce: ethers.BigNumber.from(0), - }; - - const registarSignature = await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1); - - await expect(env.multipass.connect(adr.multipassOwner.wallet).deleteName(invalidDomainQuery)).to.be.revertedWith( - 'Domain does not exist', - ); - - let applicantData: LibMultipass.RecordStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - wallet: adr.player1.wallet.address, - nonce: 0, - domainName: ethers.utils.formatBytes32String('invalidDomain'), - }; - - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .register( - applicantData, - ethers.utils.formatBytes32String('invalidDomain'), - registarSignature, - registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('Domain does not exist'); - }); - it('Reverts if intializing domain name props are wrong', async () => { - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - DEFAULT_FREE_REGISTRATIONS, - ethers.utils.parseEther('3'), - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - ethers.utils.parseEther('1.0001'), - ethers.utils.parseEther('2'), - ), - ).to.be.revertedWith('Multipass->initializeDomain: referral values are higher then fee itself'); - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - ethers.constants.AddressZero, - 1000, - ethers.utils.parseEther('3'), - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - ethers.utils.parseEther('1.0001'), - ethers.utils.parseEther('2'), - ), - ).to.be.revertedWith('Multipass->initializeDomain: You must provide a registrar address'); - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - 1000, - ethers.utils.parseEther('3'), - ethers.utils.formatBytes32String(''), - ethers.utils.parseEther('1'), - ethers.utils.parseEther('2'), - ), - ).to.be.revertedWith('Multipass->initializeDomain: Domain name cannot be empty'); - - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - 1000, - ethers.constants.MaxUint256, - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - ethers.constants.MaxUint256, - ethers.utils.parseEther('1'), - ), - ).to.be.revertedWith('Multipass->initializeDomain: referrerReward + referralDiscount overflow'); - }); - it('Reverts any ownerOnly call by not an owner', async () => { - await expect( - env.multipass.connect(adr.maliciousActor1.wallet).withrawFunds(adr.maliciousActor1.wallet.address), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - await expect( - env.multipass - .connect(adr.maliciousActor1.wallet) - .changeReferralProgram( - DEFAULT_REWARD, - DEFAULT_FREE_REGISTRATIONS, - DEFAULT_DISCOUNT, - ethers.utils.formatBytes32String(''), - ), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - await expect(env.multipass.connect(adr.maliciousActor1.wallet).deleteName(emptyUserQuery)).to.be.revertedWith( - 'LibDiamond: Must be contract owner', - ); - await expect( - env.multipass - .connect(adr.maliciousActor1.wallet) - .changeRegistrar(ethers.utils.formatBytes32String(''), adr.maliciousActor1.wallet.address), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - await expect( - env.multipass.connect(adr.maliciousActor1.wallet).changeFee(ethers.utils.formatBytes32String(''), DEFAULT_FEE), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - await expect( - env.multipass.connect(adr.maliciousActor1.wallet).activateDomain(ethers.utils.formatBytes32String('')), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - await expect( - env.multipass.connect(adr.maliciousActor1.wallet).deactivateDomain(ethers.utils.formatBytes32String('')), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - await expect( - env.multipass - .connect(adr.maliciousActor1.wallet) - .initializeDomain( - adr.maliciousActor1.wallet.address, - DEFAULT_FREE_REGISTRATIONS, - DEFAULT_FEE, - ethers.utils.formatBytes32String(''), - DEFAULT_REWARD, - DEFAULT_DISCOUNT, - ), - ).to.be.revertedWith('LibDiamond: Must be contract owner'); - }); - describe('When a new domain was initialized', () => { - let numDomains = 0; - beforeEach(async () => { - await env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - DEFAULT_FREE_REGISTRATIONS, - DEFAULT_FEE, - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - DEFAULT_REWARD, - DEFAULT_DISCOUNT, - ); - numDomains = 1; - }); - it('Reverts if domain name already registered', async () => { - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - DEFAULT_FREE_REGISTRATIONS, - DEFAULT_FEE, - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - DEFAULT_REWARD, - DEFAULT_DISCOUNT, - ), - ).to.be.revertedWith('Multipass->initializeDomain: Domain name already exists'); - }); - it('Domain name state is equal to initial values and is not active', async () => { - const resp = await env.multipass.getDomainState(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); - // expect(ethers.utils.parseBytes32String(resp)).to.be.equal( - // NEW_DOMAIN_NAME1 - // ); - expect(ethers.utils.parseBytes32String(resp['name'])).to.be.equal(NEW_DOMAIN_NAME1); - expect(resp['fee']).to.be.equal(DEFAULT_FEE); - expect(resp['freeRegistrationsNumber']).to.be.equal(DEFAULT_FREE_REGISTRATIONS); - expect(resp['referrerReward']).to.be.equal(DEFAULT_REWARD); - expect(resp['referralDiscount']).to.be.equal(DEFAULT_DISCOUNT); - expect(resp['isActive']).to.be.equal(false); - expect(resp['registrar']).to.be.equal(adr.registrar1.wallet.address); - expect(resp['ttl']).to.be.equal(0); - expect(resp['registerSize'].toString()).to.be.equal('0'); - }); - it('Incremented number of domains', async () => { - expect(await env.multipass.getContractState()).to.be.equal(numDomains); - }); - it('emits when domain activated', async () => { - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .activateDomain(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)), - ).to.emit(env.multipass, 'DomainActivated'); - }); - it('Does not allow to register because is not active', async () => { - let applicantData: LibMultipass.RecordStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - wallet: adr.player1.wallet.address, - nonce: 0, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - }; - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - applicantData, - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - ethers.constants.HashZero, - ethers.constants.HashZero, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('Multipass->register: domain is not active'); - }); - it('Emits and changes fee', async () => { - await expect( - env.multipass - .connect(adr.multipassOwner.wallet) - .changeFee(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), FEE_AFTER_CHANGE), - ).to.emit(env.multipass, 'DomainFeeChanged'); - - const resp = await env.multipass - .connect(adr.player1.wallet) - .getDomainState(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); - expect(resp[1]).to.be.equal(FEE_AFTER_CHANGE); - }); - describe('when domain was set to active', () => { - beforeEach(async () => { - await env.multipass - .connect(adr.multipassOwner.wallet) - .activateDomain(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); - }); - it('Is set to active', async () => { - const resp = await env.multipass.getDomainState(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); - expect(resp['isActive']).to.be.true; - }); - - it('Emits on register when properties are valid', async () => { - const registrarMessage = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - deadline: ethers.BigNumber.from(forkBlockNumber + 9999), - nonce: ethers.BigNumber.from(0), - }; - - const registarSignature = await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1); - - let applicantData: LibMultipass.RecordStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - wallet: adr.player1.wallet.address, - nonce: 0, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - }; - - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - applicantData, - registrarMessage.domainName, - registarSignature, - registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.emit(env.multipass, 'Registered'); - }); - - it('Reverts on register if properties are invalid', async () => { - const registrarMessage = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - deadline: ethers.BigNumber.from(forkBlockNumber + 9999), - nonce: ethers.BigNumber.from(0), - }; - - const invalidRegistrarSignature = await signRegistrarMessage( - registrarMessage, - env.multipass.address, - adr.maliciousActor1, - ); - - let applicantData: LibMultipass.RecordStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name), - id: ethers.utils.formatBytes32String(adr.player1.id), - wallet: adr.player1.wallet.address, - nonce: 0, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - }; - - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - applicantData, - registrarMessage.domainName, - invalidRegistrarSignature, - registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('Multipass->register: Registrar signature is not valid'); - - registrarMessage.deadline = ethers.BigNumber.from(ethers.provider.blockNumber); - - // await expect( - // env.multipass - // .connect(adr.player1.wallet) - // .register( - // applicantData, - // registrarMessage.domainName, - // await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1), - // registrarMessage.deadline, - // emptyUserQuery, - // ethers.constants.HashZero, - // ), - // ).to.be.revertedWith('Multipass->register: Deadline is less than current block number'); - }); - it('Reverts if signature is outdated', async () => { - const registrantProps1 = await getUserRegisterProps( - adr.player2, - adr.registrar1, - NEW_DOMAIN_NAME1, - 1, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - registrantProps1.applicantData, - registrantProps1.registrarMessage.domainName, - registrantProps1.validSignature, - registrantProps1.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('Multipass->register: Deadline is less than current block number'); - }); - it('Allows valid registrations for free until free tier has reached', async () => { - const size = DEFAULT_FREE_REGISTRATIONS.toNumber(); - const players = [adr.player1, adr.player2, adr.player3, adr.player4, adr.player5]; - let i = 0; - for (i = 0; i < size; i++) { - const regProps = await getUserRegisterProps( - players[i], - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - regProps.applicantData, - regProps.registrarMessage.domainName, - regProps.validSignature, - regProps.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.emit(env.multipass, 'Registered'); - } - const regProps = await getUserRegisterProps( - players[i], - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - regProps.applicantData, - regProps.registrarMessage.domainName, - regProps.validSignature, - regProps.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('Multipass->register: Payment value is not enough'); - }); - - describe('When user was registered', () => { - let numDomains = 0; - beforeEach(async () => { - const regProps = await getUserRegisterProps( - adr.player1, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - - await env.multipass - .connect(adr.player1.wallet) - .register( - regProps.applicantData, - regProps.registrarMessage.domainName, - regProps.validSignature, - regProps.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ); - }); - it('Can find newly registered user ', async () => { - //By full query - let query: LibMultipass.NameQueryStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), - id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), - wallet: adr.player1.wallet.address, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - targetDomain: ethers.utils.formatBytes32String(''), - }; - - let resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); - expect(resp[0]).to.be.true; - - //With id and address - query = { - name: ethers.utils.formatBytes32String(''), - id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), - wallet: adr.player1.wallet.address, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - targetDomain: ethers.utils.formatBytes32String(''), - }; - - resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); - expect(resp[0]).to.be.true; - - //With only id - query = { - name: ethers.utils.formatBytes32String(''), - id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), - wallet: ethers.constants.AddressZero, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - targetDomain: ethers.utils.formatBytes32String(''), - }; - - resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); - expect(resp[0]).to.be.true; - - //With only address - query = { - name: ethers.utils.formatBytes32String(''), - id: ethers.utils.formatBytes32String(''), - wallet: adr.player1.wallet.address, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - targetDomain: ethers.utils.formatBytes32String(''), - }; - - resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); - expect(resp[0]).to.be.true; - - //With only name - query = { - name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), - id: ethers.utils.formatBytes32String(''), - wallet: ethers.constants.AddressZero, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - targetDomain: ethers.utils.formatBytes32String(''), - }; - - resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); - expect(resp[0]).to.be.true; - }); - it('Reverts registration if user id already exist', async () => { - const regProps = await getUserRegisterProps( - adr.player1, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - regProps.applicantData, - regProps.registrarMessage.domainName, - regProps.validSignature, - regProps.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('User already registered, use modify instead'); - regProps.applicantData.id = ethers.utils.formatBytes32String(adr.player2.id); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - regProps.applicantData, - regProps.registrarMessage.domainName, - regProps.validSignature, - regProps.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('User already registered, use modify instead'); - regProps.applicantData.name = ethers.utils.formatBytes32String(adr.player2.name); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - regProps.applicantData, - regProps.registrarMessage.domainName, - regProps.validSignature, - regProps.registrarMessage.deadline, - emptyUserQuery, - ethers.constants.HashZero, - ), - ).to.be.revertedWith('User already registered, use modify instead'); - }); - it('Emits when register with valid referral code', async () => { - const registrantProps = await getUserRegisterProps( - adr.player2, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - adr.player1, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - registrantProps.applicantData, - registrantProps.registrarMessage.domainName, - registrantProps.validSignature, - registrantProps.registrarMessage.deadline, - registrantProps.referrerData, - registrantProps.referrerSignature, - ), - ).to.emit(env.multipass, 'Referred'); - }); - it('Can modify username with a valid arguments', async () => { - const registrarMessage = { - name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1 + `new`), - id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - deadline: ethers.BigNumber.from(forkBlockNumber + 99999), - nonce: ethers.BigNumber.from(1), - }; - - const modifyQuery: LibMultipass.NameQueryStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), - id: registrarMessage.id, - domainName: registrarMessage.domainName, - wallet: adr.player1.wallet.address, - targetDomain: ethers.utils.formatBytes32String(''), - }; - - const validSignature = await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1); - - await expect( - env.multipass - .connect(adr.player1.wallet) - .modifyUserName( - registrarMessage.domainName, - modifyQuery, - registrarMessage.name, - validSignature, - registrarMessage.deadline, - ), - ).to.be.revertedWith('Multipass->modifyUserName: Not enough payment'); - - const valueToPay = env.multipass.getModifyPrice(modifyQuery); - await expect( - env.multipass - .connect(adr.player1.wallet) - .modifyUserName( - registrarMessage.domainName, - modifyQuery, - registrarMessage.name, - validSignature, - registrarMessage.deadline, - { value: valueToPay }, - ), - ).to.be.emit(env.multipass, 'UserRecordModified'); - }); - it('Emits and deletes user', async () => { - let query: LibMultipass.NameQueryStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), - id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), - wallet: adr.player1.wallet.address, - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - targetDomain: ethers.utils.formatBytes32String(''), - }; - - await expect(env.multipass.connect(adr.multipassOwner.wallet).deleteName(query)).to.emit( - env.multipass, - 'nameDeleted', - ); - - let resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); - expect(resp[0]).to.be.false; - }); - describe('When second domain is initialized and active', () => { - beforeEach(async () => { - await env.multipass - .connect(adr.multipassOwner.wallet) - .initializeDomain( - adr.registrar1.wallet.address, - DEFAULT_FREE_REGISTRATIONS, - DEFAULT_FEE, - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME2), - DEFAULT_REWARD, - DEFAULT_DISCOUNT, - ); - await env.multipass - .connect(adr.multipassOwner.wallet) - .activateDomain(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME2)); - }); - it('Reverts on referring yourself from a different domain', async () => { - const registrantProps1 = await getUserRegisterProps( - adr.player2, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await env.multipass - .connect(adr.player2.wallet) - .register( - registrantProps1.applicantData, - registrantProps1.registrarMessage.domainName, - registrantProps1.validSignature, - registrantProps1.registrarMessage.deadline, - registrantProps1.referrerData, - registrantProps1.referrerSignature, - ); - const registrantProps2 = await getUserRegisterProps( - adr.player2, - adr.registrar1, - NEW_DOMAIN_NAME2, - forkBlockNumber + 99999, - env.multipass.address, - adr.player2, - NEW_DOMAIN_NAME1, - ); - const registrantProps21 = await getUserRegisterProps( - adr.player2, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - adr.player2, - ); - await expect( - env.multipass - .connect(adr.player2.wallet) - .register( - registrantProps2.applicantData, - registrantProps2.registrarMessage.domainName, - registrantProps2.validSignature, - registrantProps2.registrarMessage.deadline, - registrantProps21.referrerData, - registrantProps2.referrerSignature, - ), - ).to.revertedWith('Cannot refer yourself'); - }); - - it('Can register same user on both domains and do cross domain lookup', async () => { - const registrantProps1 = await getUserRegisterProps( - adr.player1, - adr.registrar1, - NEW_DOMAIN_NAME2, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - registrantProps1.applicantData, - registrantProps1.registrarMessage.domainName, - registrantProps1.validSignature, - registrantProps1.registrarMessage.deadline, - registrantProps1.referrerData, - registrantProps1.referrerSignature, - ), - ).to.emit(env.multipass, 'Registered'); - - const crossDomainQuery: LibMultipass.NameQueryStruct = { - name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME2), - id: ethers.utils.formatBytes32String(''), - domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME2), - wallet: ethers.constants.AddressZero, - targetDomain: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - }; - const resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(crossDomainQuery); - expect(resp[0]).to.be.true; - expect(ethers.utils.parseBytes32String(resp[1][1])).to.be.equal(adr.player1.name + `.` + NEW_DOMAIN_NAME1); - }); - }); - }); - describe('When free number of free registrations has been reached', () => { - beforeEach(async () => { - await env.multipass - .connect(adr.multipassOwner.wallet) - .changeReferralProgram( - DEFAULT_REWARD, - 0, - DEFAULT_DISCOUNT, - ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), - ); - }); - it('Should allow registering with paying ether', async () => { - const registrantProps1 = await getUserRegisterProps( - adr.player1, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - registrantProps1.applicantData, - registrantProps1.registrarMessage.domainName, - registrantProps1.validSignature, - registrantProps1.registrarMessage.deadline, - registrantProps1.referrerData, - registrantProps1.referrerSignature, - { value: DEFAULT_FEE }, - ), - ).to.emit(env.multipass, 'Registered'); - }); - it('Should be able withraw ammount payed', async () => { - const registrantProps1 = await getUserRegisterProps( - adr.player1, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - registrantProps1.applicantData, - registrantProps1.registrarMessage.domainName, - registrantProps1.validSignature, - registrantProps1.registrarMessage.deadline, - registrantProps1.referrerData, - registrantProps1.referrerSignature, - { value: DEFAULT_FEE }, - ), - ).to.emit(env.multipass, 'Registered'); - await expect( - await env.multipass.connect(adr.multipassOwner.wallet).withrawFunds(adr.multipassOwner.wallet.address), - ).to.changeEtherBalance(adr.multipassOwner.wallet, DEFAULT_FEE); - }); - it('Should revert register if not enough ether', async () => { - const registrantProps1 = await getUserRegisterProps( - adr.player1, - adr.registrar1, - NEW_DOMAIN_NAME1, - forkBlockNumber + 99999, - env.multipass.address, - ); - await expect( - env.multipass - .connect(adr.player1.wallet) - .register( - registrantProps1.applicantData, - registrantProps1.registrarMessage.domainName, - registrantProps1.validSignature, - registrantProps1.registrarMessage.deadline, - registrantProps1.referrerData, - registrantProps1.referrerSignature, - { value: NOT_ENOUGH_FEE }, - ), - ).to.be.revertedWith('Multipass->register: Payment value is not enough'); - }); - }); - }); - }); -}); diff --git a/test/Diamond.ts b/test/Diamond.ts index 0f5429e6..e6be8476 100644 --- a/test/Diamond.ts +++ b/test/Diamond.ts @@ -73,7 +73,6 @@ describe('DiamondTest', async function () { ], ethers.constants.AddressZero, '0x', - { gasLimit: 800000 }, ); receipt = await tx.wait(); if (!receipt.status) { diff --git a/test/LibCoinVending.ts b/test/LibCoinVending.ts index 6e2420d5..b931f6d1 100644 --- a/test/LibCoinVending.ts +++ b/test/LibCoinVending.ts @@ -1,4 +1,3 @@ -const { assert } = require('chai'); import { deployments, ethers } from 'hardhat'; import { expect } from 'chai'; import { MockERC1155, MockVendingMachine, MockERC20, MockERC721 } from '../types/src/mocks'; diff --git a/test/MAODistribution.ts b/test/MAODistribution.ts index 3e66b57a..265f876d 100644 --- a/test/MAODistribution.ts +++ b/test/MAODistribution.ts @@ -7,6 +7,7 @@ import { IDAO, MAODistribution, DAODistributor, Rankify, RankifyDiamondInstance import utils, { AdrSetupResult, setupTest } from './utils'; import { getCodeIdFromArtifact } from '../scripts/getCodeId'; import addDistribution from '../scripts/playbooks/addDistribution'; +import generateDistributorData from '../scripts/libraries/generateDistributorData'; describe('MAODistribution', async function () { let contract: MAODistribution; @@ -41,7 +42,6 @@ describe('MAODistribution', async function () { await addDistribution(hre)(await getCodeIdFromArtifact(hre)('MAODistribution'), signer); }); it('Can instantiate a distribution', async () => { - // Define the arguments for the instantiate function const distributorArguments: MAODistribution.DistributorArgumentsStruct = { DAOSEttings: { daoURI: 'https://example.com/dao', @@ -50,30 +50,18 @@ describe('MAODistribution', async function () { tokenName: 'tokenName', tokenSymbol: 'tokenSymbol', }, - ACIDSettings: { + RankifySettings: { RankTokenContractURI: 'https://example.com/rank', - gamePrice: 1, - joinGamePrice: 1, - maxPlayersSize: 16, - maxTurns: 1, metadata: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('metadata')), - minPlayersSize: 4, - paymentToken: rankify.address, rankTokenURI: 'https://example.com/rank', - timePerTurn: 1, - timeToJoin: 1, - voteCredits: 14, + principalCost: 1, + principalTimeConstant: 1, }, }; - // const abi = import('../abi/src/distributions/MAODistribution.sol/MAODistribution.json'); - // Encode the arguments - const data = ethers.utils.defaultAbiCoder.encode( - [ - 'tuple(tuple(string daoURI, string subdomain, bytes metadata, string tokenName, string tokenSymbol) DAOSEttings, tuple(uint256 timePerTurn, uint256 maxPlayersSize, uint256 minPlayersSize, uint256 timeToJoin, uint256 maxTurns, uint256 voteCredits, uint256 gamePrice, address paymentToken, uint256 joinGamePrice, string metadata, string rankTokenURI, string RankTokenContractURI) ACIDSettings)', - ], - [distributorArguments], - ); - // const tx = contract.instantiate(data); + + // Encode the arguments using generateDistributorData + const data = generateDistributorData(distributorArguments); + const distributorsDistId = ethers.utils.keccak256( ethers.utils.defaultAbiCoder.encode(['bytes32', 'address'], [maoId, ethers.constants.AddressZero]), ); diff --git a/test/RankToken.ts b/test/RankToken.ts index d019d75a..1114d98e 100644 --- a/test/RankToken.ts +++ b/test/RankToken.ts @@ -6,6 +6,7 @@ import { RankifyDiamondInstance, RankToken, Rankify } from '../types'; import addDistribution from '../scripts/playbooks/addDistribution'; import { getCodeIdFromArtifact } from '../scripts/getCodeId'; import { MAODistribution } from '../types/src/distributions/MAODistribution'; +import generateDistributorData from '../scripts/libraries/generateDistributorData'; let adr: AdrSetupResult; let env: EnvSetupResult; @@ -27,33 +28,21 @@ describe('Rank Token Test', async function () { tokenName: 'tokenName', tokenSymbol: 'tokenSymbol', }, - ACIDSettings: { + RankifySettings: { RankTokenContractURI: 'https://example.com/rank', - gamePrice: RInstanceSettings.RInstance_GAME_PRICE, - joinGamePrice: RInstanceSettings.RInstance_JOIN_GAME_PRICE, - maxPlayersSize: RInstanceSettings.RInstance_MAX_PLAYERS, - maxTurns: RInstanceSettings.RInstance_MAX_TURNS, + principalCost: RInstanceSettings.PRINCIPAL_COST, + principalTimeConstant: RInstanceSettings.PRINCIPAL_TIME_CONSTANT, metadata: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('metadata')), - minPlayersSize: RInstanceSettings.RInstance_MIN_PLAYERS, - paymentToken: env.rankifyToken.address, rankTokenURI: 'https://example.com/rank', - timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, - timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, - voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, }, }; - // const abi = import('../abi/src/distributions/MAODistribution.sol/MAODistribution.json'); - // Encode the arguments - const data = ethers.utils.defaultAbiCoder.encode( - [ - 'tuple(tuple(string daoURI, string subdomain, bytes metadata, string tokenName, string tokenSymbol) DAOSEttings, tuple(uint256 timePerTurn, uint256 maxPlayersSize, uint256 minPlayersSize, uint256 timeToJoin, uint256 maxTurns, uint256 voteCredits, uint256 gamePrice, address paymentToken, uint256 joinGamePrice, string metadata, string rankTokenURI, string RankTokenContractURI) ACIDSettings)', - ], - [distributorArguments], - ); + // Use generateDistributorData to encode the arguments + const data = generateDistributorData(distributorArguments); const maoCode = await hre.ethers.provider.getCode(env.maoDistribution.address); const maoId = ethers.utils.keccak256(maoCode); - const distributorsDistId = ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode(['bytes32', 'address'], [maoId, ethers.constants.AddressZero]), + const distributorsDistId = await env.distributor['calculateDistributorId(bytes32,address)']( + maoId, + ethers.constants.AddressZero, ); const token = await deployments.get('Rankify'); const { owner } = await getNamedAccounts(); @@ -89,7 +78,7 @@ describe('Rank Token Test', async function () { await env.rankifyToken.connect(adr.player9.wallet).approve(rankifyInstance.address, ethers.constants.MaxUint256); await env.rankifyToken.connect(adr.player10.wallet).approve(rankifyInstance.address, ethers.constants.MaxUint256); - rankToken = (await ethers.getContractAt('RankToken', evts[0].args.instances[13])) as RankToken; + rankToken = (await ethers.getContractAt('RankToken', evts[0].args.instances[12])) as RankToken; }); // it('Allows only owner to set rankingInstance', async () => { // await expect(rankToken.connect(deployer).updateRankingInstance(adr.gameCreator1.wallet.address)) diff --git a/test/RankifyInstance.ts b/test/RankifyInstance.ts index 813b84d6..8f3f425f 100644 --- a/test/RankifyInstance.ts +++ b/test/RankifyInstance.ts @@ -4,7 +4,7 @@ import { RInstance_MIN_PLAYERS, EnvSetupResult, MockVotes, - ProposalSubmittion, + ProposalSubmission, setupTest, SignerIdentity, RInstance_MAX_TURNS, @@ -14,10 +14,9 @@ import { expect } from 'chai'; import { time } from '@nomicfoundation/hardhat-network-helpers'; import { Rankify, RankifyDiamondInstance, RankToken } from '../types/'; import { LibCoinVending } from '../types/src/facets/RankifyInstanceRequirementsFacet'; -import { IRankifyInstanceCommons } from '../types/src/facets/RankifyInstanceMainFacet'; +import { IRankifyInstance } from '../types/src/facets/RankifyInstanceMainFacet'; import { deployments, ethers, getNamedAccounts } from 'hardhat'; const path = require('path'); -// import { TokenMust, TokenTypes } from "../types/enums"; import { BigNumber, BigNumberish } from 'ethers'; import { assert } from 'console'; import { solidityKeccak256 } from 'ethers/lib/utils'; @@ -28,8 +27,9 @@ const scriptName = path.basename(__filename); import { getCodeIdFromArtifact } from '../scripts/getCodeId'; import { MAODistribution } from '../types/src/distributions/MAODistribution'; +import generateDistributorData from '../scripts/libraries/generateDistributorData'; let votes: MockVotes; -let proposalsStruct: ProposalSubmittion[]; +let proposalsStruct: ProposalSubmission[]; let adr: AdrSetupResult; let votersAddresses: string[]; let env: EnvSetupResult; @@ -44,10 +44,19 @@ const createGame = async ( openNow?: boolean, ) => { await env.rankifyToken.connect(signer.wallet).approve(gameContract.address, ethers.constants.MaxUint256); - await gameContract.connect(signer.wallet)['createGame(address,uint256)'](gameMaster, gameRank); - const gameId = await gameContract - .getContractState() - .then((state: IRankifyInstanceCommons.RInstanceStateStructOutput) => state.BestOfState.numGames); + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: gameMaster, + gameRank: gameRank, + maxPlayerCnt: RInstance_MAX_PLAYERS, + minPlayerCnt: RInstance_MIN_PLAYERS, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + nTurns: RInstance_MAX_TURNS, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + minGameTime: RInstanceSettings.RInstance_MIN_GAME_TIME, + }; + await gameContract.connect(signer.wallet).createGame(params); + const gameId = await gameContract.getContractState().then(state => state.numGames); if (openNow) await gameContract.connect(signer.wallet).openRegistration(gameId); return gameId; }; @@ -58,17 +67,17 @@ const runToTheEnd = async ( players: [SignerIdentity, SignerIdentity, ...SignerIdentity[]], distribution?: 'ftw' | 'semiUniform' | 'equal', ) => { - // console.log('running to the end'); - // const initialTurn = await rankifyInstance.getTurn(gameId); let isGameOver = await rankifyInstance.isGameOver(gameId); + let isLastTurn = await rankifyInstance.isLastTurn(gameId); while (!isGameOver) { + isLastTurn = await rankifyInstance.isLastTurn(gameId); const turn = await rankifyInstance.getTurn(gameId).then(r => r.toNumber()); - // console.log('running to the end', turn, isLastTurn, isGameOver); if (turn !== 1) { votes = await mockValidVotes(players, gameContract, gameId, gameMaster, true, distribution ?? 'ftw'); } const proposals = await mockValidProposals(players, gameContract, gameMaster, gameId, true); + if (isLastTurn) await time.increase(RInstanceSettings.RInstance_MIN_GAME_TIME + 1); await gameContract.connect(gameMaster.wallet).endTurn( gameId, turn == 1 ? [] : votes?.map(vote => vote.vote), @@ -86,7 +95,6 @@ const runToLastTurn = async ( distribution?: 'ftw' | 'semiUniform' | 'equal', ): Promise => { const initialTurn = await rankifyInstance.getTurn(gameId); - // console.log("running to last turn, initial: ", initialTurn.toString()); for (let turn = initialTurn.toNumber(); turn < RInstanceSettings.RInstance_MAX_TURNS; turn++) { if (turn !== 1) { votes = await mockValidVotes(players, gameContract, gameId, gameMaster, true, distribution ?? 'ftw'); @@ -105,8 +113,7 @@ const runToLastTurn = async ( }; const endTurn = async (gameId: BigNumberish, gameContract: RankifyDiamondInstance) => { - const turn = await gameContract.getTurn(gameId); - await gameContract.connect(adr.gameMaster1.wallet).endTurn( + return gameContract.connect(adr.gameMaster1.wallet).endTurn( gameId, votes.map(vote => vote.vote), proposalsStruct.map(prop => prop.proposal), @@ -133,55 +140,6 @@ const runToOvertime = async ( ); }; -// const runToTheEnd = async ( -// gameId: BigNumberish, -// gameContract: RankifyDiamondInstance, -// gameMaster: SignerIdentity, -// players: [SignerIdentity, SignerIdentity, ...SignerIdentity[]] -// ) => { - -// await runToLastTurn(gameId, gameContract, gameMaster, players); - -// await mockValidVotes(players, gameContract, gameId, gameMaster, true, "ftw"); -// let turn = await gameContract.getTurn(gameId); -// console.log("attempt to finish last turn", turn.toString()); -// await gameContract.connect(gameMaster.wallet).endTurn( -// gameId, -// getTurnSalt({ -// gameId: gameId, -// turn: turn, -// }), -// votersAddresses, -// votes.map((vote) => vote.vote) -// ); -// turn = await gameContract.getTurn(gameId); -// const isOvertime = await rankifyInstance.isOvertime(gameId); -// const isGameOver = await rankifyInstance.isGameOver(gameId); -// while (!isGameOver) { -// console.log("running isGameOver", isGameOver, isOvertime, turn.toString()); -// assert(isOvertime, "should be ovetime, now?"); -// await mockValidVotes( -// players, -// gameContract, -// gameId, -// gameMaster, -// true, -// "ftw" -// ); -// await mockValidProposals(players, gameContract, gameMaster, gameId, true); -// await gameContract.connect(gameMaster.wallet).endTurn( -// gameId, -// getTurnSalt({ -// gameId: gameId, -// turn: turn, -// }), -// votersAddresses, -// votes.map((vote) => vote.vote) -// ); -// } -// console.log("runToTheEnd", turn); -// }; - const mockValidVotes = async ( players: [SignerIdentity, SignerIdentity, ...SignerIdentity[]], gameContract: RankifyDiamondInstance, @@ -210,20 +168,11 @@ const mockValidVotes = async ( return votes; }; -const startGame = async ( - gameId: BigNumberish, - // players: [SignerIdentity, SignerIdentity, ...SignerIdentity[]] -) => { +const startGame = async (gameId: BigNumberish) => { const currentT = await time.latest(); await time.setNextBlockTimestamp(currentT + Number(RInstanceSettings.RInstance_TIME_TO_JOIN) + 1); await mineBlocks(RInstanceSettings.RInstance_TIME_TO_JOIN + 1); await rankifyInstance.connect(adr.gameMaster1.wallet).startGame(gameId); - // proposalsStruct = await mockProposals({ - // players: players, - // gameId: 1, - // turn: 1, - // verifierAddress: rankifyInstance.address, - // }); }; const mockValidProposals = async ( @@ -235,7 +184,6 @@ const mockValidProposals = async ( ) => { const turn = await gameContract.getTurn(gameId); - // getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS) proposalsStruct = await mockProposals({ players: players, gameId: gameId, @@ -260,8 +208,7 @@ const fillParty = async ( gameMaster?: SignerIdentity, ) => { for (let i = 0; i < players.length; i++) { - // let name = `player${i}` as any as keyof AdrSetupResult; - if (!env.rankTokenBase.address) throw new Error('Rank token undefined or undeployed'); + if (!env.rankTokenBase.address) throw new Error('Rank token undefined or unemployed'); await rankToken.connect(players[i].wallet).setApprovalForAll(rankifyInstance.address, true); await gameContract.connect(players[i].wallet).joinGame(gameId, { value: ethers.utils.parseEther('0.4') }); } @@ -299,33 +246,20 @@ describe(scriptName, () => { tokenName: 'tokenName', tokenSymbol: 'tokenSymbol', }, - ACIDSettings: { + RankifySettings: { RankTokenContractURI: 'https://example.com/rank', - gamePrice: RInstanceSettings.RInstance_GAME_PRICE, - joinGamePrice: RInstanceSettings.RInstance_JOIN_GAME_PRICE, - maxPlayersSize: RInstanceSettings.RInstance_MAX_PLAYERS, - maxTurns: RInstanceSettings.RInstance_MAX_TURNS, metadata: ethers.utils.hexlify(ethers.utils.toUtf8Bytes('metadata')), - minPlayersSize: RInstanceSettings.RInstance_MIN_PLAYERS, - paymentToken: env.rankifyToken.address, rankTokenURI: 'https://example.com/rank', - timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, - timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, - voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + principalCost: RInstanceSettings.PRINCIPAL_COST, + principalTimeConstant: RInstanceSettings.PRINCIPAL_TIME_CONSTANT, }, }; - // const abi = import('../abi/src/distributions/MAODistribution.sol/MAODistribution.json'); - // Encode the arguments - const data = ethers.utils.defaultAbiCoder.encode( - [ - 'tuple(tuple(string daoURI, string subdomain, bytes metadata, string tokenName, string tokenSymbol) DAOSEttings, tuple(uint256 timePerTurn, uint256 maxPlayersSize, uint256 minPlayersSize, uint256 timeToJoin, uint256 maxTurns, uint256 voteCredits, uint256 gamePrice, address paymentToken, uint256 joinGamePrice, string metadata, string rankTokenURI, string RankTokenContractURI) ACIDSettings)', - ], - [distributorArguments], - ); + const data = generateDistributorData(distributorArguments); const maoCode = await hre.ethers.provider.getCode(env.maoDistribution.address); const maoId = ethers.utils.keccak256(maoCode); - const distributorsDistId = ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode(['bytes32', 'address'], [maoId, ethers.constants.AddressZero]), + const distributorsDistId = await env.distributor['calculateDistributorId(bytes32,address)']( + maoId, + ethers.constants.AddressZero, ); const token = await deployments.get('Rankify'); @@ -363,7 +297,7 @@ describe(scriptName, () => { await env.rankifyToken.connect(adr.player9.wallet).approve(rankifyInstance.address, ethers.constants.MaxUint256); await env.rankifyToken.connect(adr.player10.wallet).approve(rankifyInstance.address, ethers.constants.MaxUint256); - rankToken = (await ethers.getContractAt('RankToken', evts[0].args.instances[13])) as RankToken; + rankToken = (await ethers.getContractAt('RankToken', evts[0].args.instances[12])) as RankToken; requirement.contracts = []; requirement.contracts.push({ @@ -404,64 +338,60 @@ describe(scriptName, () => { }, }); }); - it('Is owned by distributor contract', async () => { - expect(await rankifyInstance.owner()).to.be.equal(env.arguableVotingTournamentDistribution.address); - }); it('Has correct initial settings', async () => { + const { DAO } = await getNamedAccounts(); const state = await rankifyInstance.connect(adr.gameCreator1.wallet).getContractState(); - expect(state.BestOfState.gamePrice).to.be.equal(RInstanceSettings.RInstance_GAME_PRICE); - expect(state.BestOfState.joinGamePrice).to.be.equal(RInstanceSettings.RInstance_JOIN_GAME_PRICE); - expect(state.BestOfState.numGames).to.be.equal(0); - expect(state.BestOfState.rankTokenAddress).to.be.equal(rankToken.address); - expect(state.TBGSEttings.maxTurns).to.be.equal(RInstanceSettings.RInstance_MAX_TURNS); - expect(state.TBGSEttings.timePerTurn).to.be.equal(RInstanceSettings.RInstance_TIME_PER_TURN); - expect(state.TBGSEttings.minPlayersSize).to.be.equal(RInstanceSettings.RInstance_MIN_PLAYERS); - expect(state.TBGSEttings.timeToJoin).to.be.equal(RInstanceSettings.RInstance_TIME_TO_JOIN); - expect(state.TBGSEttings.maxTurns).to.be.equal(RInstanceSettings.RInstance_MAX_TURNS); + expect(state.commonParams.principalTimeConstant).to.be.equal(RInstanceSettings.PRINCIPAL_TIME_CONSTANT); + expect(state.commonParams.principalCost).to.be.equal(RInstanceSettings.PRINCIPAL_COST); + expect(state.commonParams.beneficiary).to.be.equal(DAO); + expect(state.commonParams.rankTokenAddress).to.be.equal(rankToken.address); }); - it('Transfer ownership can be done only by contract owner', async () => { - const impersonatedSigner = await ethers.getImpersonatedSigner(env.arguableVotingTournamentDistribution.address); - await network.provider.send('hardhat_setBalance', [impersonatedSigner.address, '0x9000000000000000000']); - - await expect( - rankifyInstance.connect(impersonatedSigner).transferOwnership(adr.gameCreator1.wallet.address), - ).to.emit(rankifyInstance, 'OwnershipTransferred(address,address)'); - + it('Ownership is renounced', async () => { + expect(await rankifyInstance.owner()).to.be.equal(ethers.constants.AddressZero); await expect( rankifyInstance.connect(adr.maliciousActor1.wallet).transferOwnership(adr.gameCreator1.wallet.address), ).to.revertedWith('LibDiamond: Must be contract owner'); }); it('has rank token assigned', async () => { const state = await rankifyInstance.getContractState(); - expect(state.BestOfState.rankTokenAddress).to.be.equal(rankToken.address); + expect(state.commonParams.rankTokenAddress).to.be.equal(rankToken.address); }); it('Can create game only with valid payments', async () => { await env.rankifyToken.connect(adr.gameCreator1.wallet).approve(rankifyInstance.address, 0); - await expect( - rankifyInstance - .connect(adr.gameCreator1.wallet) - ['createGame(address,uint256)'](adr.gameMaster1.wallet.address, 1), - ).to.revertedWithCustomError(env.rankifyToken, 'ERC20InsufficientAllowance'); + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 1, + maxPlayerCnt: RInstance_MAX_PLAYERS, + minPlayerCnt: RInstance_MIN_PLAYERS, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + nTurns: RInstance_MAX_TURNS, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + minGameTime: RInstanceSettings.RInstance_MIN_GAME_TIME, + }; + + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).to.be.revertedWithCustomError( + env.rankifyToken, + 'ERC20InsufficientAllowance', + ); await env.rankifyToken .connect(adr.gameCreator1.wallet) .approve(rankifyInstance.address, ethers.constants.MaxUint256); - await expect( - rankifyInstance - .connect(adr.gameCreator1.wallet) - ['createGame(address,uint256)'](adr.gameMaster1.wallet.address, 1), - ).to.emit(rankifyInstance, 'gameCreated'); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).to.emit( + rankifyInstance, + 'gameCreated', + ); await env.rankifyToken .connect(adr.gameCreator1.wallet) .burn(await env.rankifyToken.balanceOf(adr.gameCreator1.wallet.address)); - await expect( - rankifyInstance - .connect(adr.gameCreator1.wallet) - ['createGame(address,uint256)'](adr.gameMaster1.wallet.address, 1), - ).to.revertedWithCustomError(env.rankifyToken, 'ERC20InsufficientBalance'); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).to.revertedWithCustomError( + env.rankifyToken, + 'ERC20InsufficientBalance', + ); }); it('Cannot perform actions on games that do not exist', async () => { - await expect(rankifyInstance.connect(adr.gameCreator1.wallet).joinGame(1)).to.be.revertedWith('no game found'); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).joinGame(1)).to.be.revertedWith('game not found'); proposalsStruct = await mockProposals({ players: getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), gameId: 1, @@ -471,7 +401,7 @@ describe(scriptName, () => { }); await expect( rankifyInstance.connect(adr.gameMaster1.wallet).submitProposal(proposalsStruct[0].params), - ).to.be.revertedWith('no game found'); + ).to.be.revertedWith('game not found'); votersAddresses = getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS).map(player => player.wallet.address); votes = await mockVotes({ gameId: 1, @@ -483,26 +413,13 @@ describe(scriptName, () => { }); await expect( rankifyInstance.connect(adr.gameMaster1.wallet).submitVote(1, votes[0].voteHidden, adr.player1.wallet.address), - ).to.be.revertedWith('no game found'); + ).to.be.revertedWith('game not found'); await expect(rankifyInstance.connect(adr.gameMaster1.wallet).openRegistration(1)).to.be.revertedWith( - 'no game found', + 'game not found', ); - // await expect( - // rankifyInstance.connect(adr.gameMaster1.wallet).addJoinRequirements(0, { - // token: { tokenAddress: ZERO_ADDRESS, tokenType: 0, tokenId: 1 }, - // amount: 1, - // must: 0, - // requireParticularERC721: false, - // }) - // ).to.be.revertedWith("no game found"); - // await expect( - // rankifyInstance.connect(adr.gameMaster1.wallet).removeJoinRequirement(0, 0) - // ).to.be.revertedWith("no game found"); - // await expect( - // rankifyInstance.connect(adr.gameMaster1.wallet).popJoinRequirements(0) - // ).to.be.revertedWith("no game found"); - await expect(rankifyInstance.connect(adr.gameMaster1.wallet).joinGame(0)).to.be.revertedWith('no game found'); - await expect(rankifyInstance.connect(adr.gameMaster1.wallet).startGame(0)).to.be.revertedWith('no game found'); + + await expect(rankifyInstance.connect(adr.gameMaster1.wallet).joinGame(0)).to.be.revertedWith('game not found'); + await expect(rankifyInstance.connect(adr.gameMaster1.wallet).startGame(0)).to.be.revertedWith('game not found'); const proposals = await mockProposals({ players: getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), gameId: 1, @@ -517,34 +434,114 @@ describe(scriptName, () => { proposals.map(prop => prop.proposal), proposalsStruct.map((p, idx) => idx), ), - ).to.be.revertedWith('no game found'); + ).to.be.revertedWith('game not found'); await expect( rankifyInstance.connect(adr.gameMaster1.wallet).submitProposal(proposalsStruct[0].params), - ).to.be.revertedWith('no game found'); + ).to.be.revertedWith('game not found'); }); - // it('Succedes to create ranked game only if sender has correspoding tier rank token', async () => { - // await expect( - // rankifyInstance - // .connect(adr.maliciousActor1.wallet) - // ['createGame(address,uint256)'](adr.gameMaster1.wallet.address, 2, { - // value: RInstanceSettings.RInstance_GAME_PRICE, - // }), - // ).to.be.revertedWith('Must have rank token'); - // }); describe('When a game of first rank was created', () => { + let initialCreatorBalance: BigNumber; + let initialBeneficiaryBalance: BigNumber; + let initialTotalSupply: BigNumber; + let gamePrice: BigNumber; + beforeEach(async () => { + // Get initial balances + initialCreatorBalance = await env.rankifyToken.balanceOf(adr.gameCreator1.wallet.address); + initialBeneficiaryBalance = await env.rankifyToken.balanceOf( + await rankifyInstance.getContractState().then(s => s.commonParams.beneficiary), + ); + initialTotalSupply = await env.rankifyToken.totalSupply(); + + // Get common params for price calculation + const { commonParams } = await rankifyInstance.getContractState(); + const { principalTimeConstant } = commonParams; + const minGameTime = principalTimeConstant; // Using same value for simplicity + gamePrice = commonParams.principalCost.mul(principalTimeConstant).div(minGameTime); + + // Create the game await createGame(rankifyInstance, adr.gameCreator1, adr.gameMaster1.wallet.address, 1); }); + + it('Should handle game creation costs and token distribution correctly', async () => { + const finalCreatorBalance = await env.rankifyToken.balanceOf(adr.gameCreator1.wallet.address); + const finalBeneficiaryBalance = await env.rankifyToken.balanceOf( + await rankifyInstance.getContractState().then(s => s.commonParams.beneficiary), + ); + const finalTotalSupply = await env.rankifyToken.totalSupply(); + + // Check creator's balance is reduced by game cost + expect(finalCreatorBalance).to.equal( + initialCreatorBalance.sub(gamePrice), + "Creator's balance should be reduced by game cost", + ); + + // Check beneficiary receives 10% of game cost + const beneficiaryShare = gamePrice.mul(10).div(100); + expect(finalBeneficiaryBalance).to.equal( + initialBeneficiaryBalance.add(beneficiaryShare), + 'Beneficiary should receive 10% of game cost', + ); + + // Check 90% of game cost is burned + const burnedAmount = gamePrice.mul(90).div(100); + expect(finalTotalSupply).to.equal(initialTotalSupply.sub(burnedAmount), '90% of game cost should be burned'); + }); + + it('Should calculate game price correctly for different time parameters', async () => { + const { commonParams } = await rankifyInstance.getContractState(); + + // Test cases with different time parameters + const testCases = [ + { minGameTime: commonParams.principalTimeConstant }, + { minGameTime: commonParams.principalTimeConstant.mul(2) }, + { minGameTime: commonParams.principalTimeConstant.div(2) }, + ]; + + for (const testCase of testCases) { + const expectedPrice = commonParams.principalCost + .mul(commonParams.principalTimeConstant) + .div(testCase.minGameTime); + + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 1, + maxPlayerCnt: RInstanceSettings.RInstance_MAX_PLAYERS, + minPlayerCnt: RInstanceSettings.RInstance_MIN_PLAYERS, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + nTurns: RInstance_MAX_TURNS, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + minGameTime: testCase.minGameTime, + }; + const { DAO } = await getNamedAccounts(); + const totalSupplyBefore = await env.rankifyToken.totalSupply(); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).changeTokenBalances( + env.rankifyToken, + [adr.gameCreator1.wallet.address, DAO], + [expectedPrice.mul(-1), expectedPrice.mul(10).div(100)], + ); + expect(await env.rankifyToken.totalSupply()).to.be.equal(totalSupplyBefore.sub(expectedPrice.mul(90).div(100))); + // Get actual game price + const actualPrice = await rankifyInstance.estimateGamePrice(testCase.minGameTime); + + // Allow for small rounding differences due to division + const difference = expectedPrice.sub(actualPrice).abs(); + expect(difference.lte(1)).to.be.true; + } + }); + it('GM is correct', async () => { expect(await rankifyInstance.getGM(1)).to.be.equal(adr.gameMaster1.wallet.address); }); it('Incremented number of games correctly', async () => { const state = await rankifyInstance.connect(adr.gameCreator1.wallet).getContractState(); - expect(state.BestOfState.numGames).to.be.equal(1); + expect(state.numGames).to.be.equal(1); }); it('Players cannot join until registration is open', async () => { await env.rankifyToken.connect(adr.player1.wallet).approve(rankifyInstance.address, ethers.constants.MaxUint256); - await expect(rankifyInstance.connect(adr.player1.wallet).joinGame(1)).to.be.revertedWith( + const gameId = await rankifyInstance.getContractState().then(s => s.numGames); + await expect(rankifyInstance.connect(adr.player1.wallet).joinGame(gameId)).to.be.revertedWith( 'addPlayer->cant join now', ); }); @@ -558,7 +555,7 @@ describe(scriptName, () => { ).to.be.revertedWith('Only game creator'); await expect( rankifyInstance.connect(adr.maliciousActor1.wallet).setJoinRequirements(11, requirement), - ).to.be.revertedWith('no game found'); + ).to.be.revertedWith('game not found'); }); it('Only game creator can open registration', async () => { await expect(rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(1)).to.be.emit( @@ -573,6 +570,7 @@ describe(scriptName, () => { beforeEach(async () => { await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(1); }); + it('Mutating join requirements is no longer possible', async () => { await expect( rankifyInstance.connect(adr.gameCreator1.wallet).setJoinRequirements(1, requirement), @@ -611,11 +609,6 @@ describe(scriptName, () => { 'addPlayer->party full', ); }); - // it('Game cannot start too early', async () => { - // await expect(rankifyInstance.connect(adr.gameMaster1.wallet).startGame(1)).to.be.revertedWith( - // 'startGame->Still Can Join', - // ); - // }); it('Game methods beside join and start are inactive', async () => { await expect( rankifyInstance.connect(adr.gameMaster1.wallet).submitProposal({ @@ -737,6 +730,21 @@ describe(scriptName, () => { it('First turn has started', async () => { expect(await rankifyInstance.connect(adr.player1.wallet).getTurn(1)).to.be.equal(1); }); + it('Cannot end game before minimum game time', async () => { + const players = getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS); + await runToLastTurn(1, rankifyInstance, adr.gameMaster1, players); + await mockValidVotes(players, rankifyInstance, 1, adr.gameMaster1, true, 'ftw'); + await mockValidProposals(players, rankifyInstance, adr.gameMaster1, 1, true); + await expect(endTurn(1, rankifyInstance)).to.be.revertedWith( + 'Game duration less than minimum required time', + ); + await time.increase(RInstanceSettings.RInstance_MIN_GAME_TIME - 100); + await expect(endTurn(1, rankifyInstance)).to.be.revertedWith( + 'Game duration less than minimum required time', + ); + await time.increase(101); + await expect(endTurn(1, rankifyInstance)).to.not.be.reverted; + }); it('Accepts only proposals and no votes', async () => { const proposals = await mockProposals({ players: getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS), @@ -881,59 +889,6 @@ describe(scriptName, () => { ethers.BigNumber.from('0'), ]); }); - // it("throws if player voting himself", async () => { - // proposalsStruct = await mockValidProposals( - // getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS), - // rankifyInstance, - // adr.gameMaster1, - // 1, - // true - // ); - // const badVote = await mockVote({ - // voter: adr.player1, - // gm: adr.gameMaster1, - // gameId: 1, - // verifierAddress: rankifyInstance.address, - // turn: 2, - // vote: [0, 1, 2], - // }); - - // const badVotes = await mockVotes({ - // gameId: 1, - // turn: 2, - // verifierAddress: rankifyInstance.address, - // players: getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS), - // gm: adr.gameMaster1, - // distribution: "semiUniform", - // }); - // badVotes[0] = badVote; - // votersAddresses = getPlayers( - // adr, - // RInstanceSettings.RInstance_MIN_PLAYERS - // ).map((player, idx) => player.wallet.address); - // for (let i = 0; i < votersAddresses.length; i++) { - // let name = `player${i + 1}` as any as keyof AdrSetupResult; - - // await rankifyInstance - // .connect(adr[`${name}`].wallet) - // .submitVote( - // 1, - // badVotes[i].voteHidden, - // badVotes[i].proof, - // badVotes[i].publicSignature - // ); - // } - - // await mineBlocks(RInstanceSettings.RInstance_TIME_PER_TURN + 1); - // await expect( - // rankifyInstance.connect(adr.gameMaster1.wallet).endTurn( - // 1, - // badVotes.map((vote) => vote.vote), - // proposalsStruct.map((p) => p.proposal), - // proposalsStruct.map((p, idx) => idx) - // ) - // ).to.be.revertedWith("voted for himself"); - // }); describe('When all players voted', () => { beforeEach(async () => { votes = await mockValidVotes( @@ -958,14 +913,12 @@ describe(scriptName, () => { ).to.be.revertedWith('nextTurn->CanEndEarly'); }); it('Can end turn if timeout reached', async () => { - // await mineBlocks(RInstanceSettings.RInstance_TIME_PER_TURN + 1); const currentT = await time.latest(); await time.setNextBlockTimestamp(currentT + Number(RInstanceSettings.RInstance_TIME_PER_TURN) + 1); expect(await rankifyInstance.getTurn(1)).to.be.equal(2); const players = getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS); const expectedScores: number[] = players.map(v => 0); for (let i = 0; i < players.length; i++) { - // expectedScores[i] = 0; if (votes.length > i) { votes[i].vote.forEach((vote, idx) => { expectedScores[idx] += Number(vote); @@ -974,18 +927,6 @@ describe(scriptName, () => { //somebody did not vote at all } } - // console.log( - // 'expectedScores', - // expectedScores, - // votes.map(vote => vote.vote), - // ); - // const tx = await rankifyInstance.connect(adr.gameMaster1.wallet).endTurn( - // 1, - // votes.map(vote => vote.vote), - // [], - // votersAddresses.map((p, idx) => idx), - // ); - // console.log((await tx.wait(1)).events?.find(e => e.event === 'TurnEnded').args); await expect( rankifyInstance.connect(adr.gameMaster1.wallet).endTurn( 1, @@ -1012,7 +953,6 @@ describe(scriptName, () => { const players = getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS); const expectedScores: number[] = players.map(v => 0); for (let i = 0; i < players.length; i++) { - // expectedScores[i] = 0; if (votes.length > i) { votes[i].vote.forEach((vote, idx) => { expectedScores[idx] += Number(vote); @@ -1050,14 +990,12 @@ describe(scriptName, () => { expect(expectedScores).to.be.eql(scores); }); it('Emits correct ProposalScore event values', async () => { - // await mineBlocks(RInstanceSettings.RInstance_TIME_PER_TURN + 1); const currentT = await time.latest(); await time.setNextBlockTimestamp(currentT + Number(RInstanceSettings.RInstance_TIME_PER_TURN) + 1); expect(await rankifyInstance.getTurn(1)).to.be.equal(2); const players = getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS); const expectedScores: number[] = players.map(v => 0); for (let i = 0; i < players.length; i++) { - // expectedScores[i] = 0; if (votes.length > i) { votes[i].vote.forEach((vote, idx) => { expectedScores[idx] += Number(vote); @@ -1112,88 +1050,6 @@ describe(scriptName, () => { }); }); }); - describe('When registration was open with additional join requirements', () => { - beforeEach(async () => { - await rankifyInstance.connect(adr.gameCreator1.wallet).setJoinRequirements(1, requirement); - await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(1); - const players = getPlayers(adr, RInstance_MAX_PLAYERS, 0); - for (let i = 0; i < players.length; i++) { - await env.mockERC1155 - .connect(adr.contractDeployer.wallet) - .mint(players[i].wallet.address, ethers.utils.parseEther('10'), '1', '0x'); - await env.mockERC20 - .connect(adr.contractDeployer.wallet) - .mint(players[i].wallet.address, ethers.utils.parseEther('10')); - await env.mockERC721.connect(adr.contractDeployer.wallet).mint(players[i].wallet.address, i + 1, '0x'); - await env.mockERC20 - .connect(players[i].wallet) - .approve(rankifyInstance.address, ethers.utils.parseEther('100')); - await env.mockERC1155.connect(players[i].wallet).setApprovalForAll(rankifyInstance.address, true); - await env.mockERC721.connect(players[i].wallet).setApprovalForAll(rankifyInstance.address, true); - } - }); - it('Fulfills funding requirement on join', async () => { - await env.mockERC20 - .connect(adr.player1.wallet) - .approve(rankifyInstance.address, ethers.utils.parseEther('100')); - const balance1155 = await env.mockERC1155.balanceOf(rankifyInstance.address, '1'); - await rankifyInstance.connect(adr.player1.wallet).joinGame(1, { value: ethers.utils.parseEther('0.4') }); - expect(await env.mockERC1155.balanceOf(rankifyInstance.address, '1')).to.be.equal( - ethers.utils.parseEther('0.4'), - ); - expect(await env.mockERC20.balanceOf(rankifyInstance.address)).to.be.equal(ethers.utils.parseEther('0.4')); - }); - it('Returns requirements on leave', async () => { - await env.mockERC20 - .connect(adr.player1.wallet) - .approve(rankifyInstance.address, ethers.utils.parseEther('100')); - await rankifyInstance.connect(adr.player1.wallet).joinGame(1, { value: ethers.utils.parseEther('0.4') }); - await rankifyInstance.connect(adr.player1.wallet).leaveGame(1); - expect(await env.mockERC1155.balanceOf(adr.player1.wallet.address, '1')).to.be.equal( - ethers.utils.parseEther('10'), - ); - expect(await env.mockERC20.balanceOf(adr.player1.wallet.address)).to.be.equal(ethers.utils.parseEther('10')); - }); - it('Returns requirements on game closed', async () => { - await env.mockERC20 - .connect(adr.player1.wallet) - .approve(rankifyInstance.address, ethers.utils.parseEther('100')); - await rankifyInstance.connect(adr.player1.wallet).joinGame(1, { value: ethers.utils.parseEther('0.4') }); - expect(await rankifyInstance.connect(adr.gameCreator1.wallet).cancelGame(1)).to.changeEtherBalance( - adr.player1.wallet.address, - ethers.utils.parseEther('0.4'), - ); - expect(await env.mockERC1155.balanceOf(adr.player1.wallet.address, '1')).to.be.equal( - ethers.utils.parseEther('10'), - ); - expect(await env.mockERC20.balanceOf(adr.player1.wallet.address)).to.be.equal(ethers.utils.parseEther('10')); - }); - it('Distributes rewards correctly when game is over', async () => { - await fillParty(getPlayers(adr, RInstance_MIN_PLAYERS, 0), rankifyInstance, 1, true, true, adr.gameMaster1); - const balanceBefore1155 = await env.mockERC1155.balanceOf(adr.player1.wallet.address, '1'); - const balanceBefore20 = await env.mockERC20.balanceOf(adr.player1.wallet.address); - const creatorBalanceBefore20 = await env.mockERC20.balanceOf(adr.gameCreator1.wallet.address); - - const creatorBalanceBefore1155 = await env.mockERC1155.balanceOf(adr.gameCreator1.wallet.address, '1'); - await runToTheEnd(1, rankifyInstance, adr.gameMaster1, getPlayers(adr, RInstance_MIN_PLAYERS, 0), 'ftw'); - expect(await env.mockERC20.balanceOf(adr.player1.wallet.address)).to.be.equal( - balanceBefore20 - .add(ethers.utils.parseEther('0.1').mul(RInstance_MIN_PLAYERS)) - .add(ethers.utils.parseEther('0.1')), // Value to lock - ); - expect(await env.mockERC20.balanceOf(adr.gameCreator1.wallet.address)).to.be.equal( - creatorBalanceBefore20.add(ethers.utils.parseEther('0.1').mul(RInstance_MIN_PLAYERS)), - ); - expect(await env.mockERC1155.balanceOf(adr.player1.wallet.address, '1')).to.be.equal( - balanceBefore1155 - .add(ethers.utils.parseEther('0.1').mul(RInstance_MIN_PLAYERS)) - .add(ethers.utils.parseEther('0.1')), // Value to lock - ); - expect(await env.mockERC1155.balanceOf(adr.gameCreator1.wallet.address, '1')).to.be.equal( - creatorBalanceBefore1155.add(ethers.utils.parseEther('0.1').mul(RInstance_MIN_PLAYERS)), - ); - }); - }); describe('When it is last turn and equal scores', () => { beforeEach(async () => { await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(1); @@ -1235,7 +1091,7 @@ describe(scriptName, () => { expect(await rankifyInstance.isOvertime(1)).to.be.true; }); - describe('when is ovetime', () => { + describe('when is overtime', () => { beforeEach(async () => { await mockValidVotes( getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), @@ -1254,7 +1110,7 @@ describe(scriptName, () => { ); await endTurn(1, rankifyInstance); }); - it('emits game Over when submited votes result unique leaders', async () => { + it('emits game Over when submitted votes result unique leaders', async () => { await mockValidVotes( getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), rankifyInstance, @@ -1270,6 +1126,7 @@ describe(scriptName, () => { 1, true, ); + await time.increase(Number(RInstanceSettings.RInstance_MIN_GAME_TIME) + 1); expect( await rankifyInstance.connect(adr.gameMaster1.wallet).endTurn( 1, @@ -1279,7 +1136,7 @@ describe(scriptName, () => { ), ).to.emit(rankifyInstance, 'GameOver'); }); - it("Keeps game in overtime when submited votes don't result unique leaders", async () => { + it("Keeps game in overtime when submitted votes don't result unique leaders", async () => { await mockValidVotes( getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), rankifyInstance, @@ -1327,14 +1184,11 @@ describe(scriptName, () => { gm: adr.gameMaster1, }); - const isover = await rankifyInstance.isGameOver(1); - for (let i = 0; i < RInstanceSettings.RInstance_MAX_PLAYERS; i++) { - const proposals = await expect( + await expect( rankifyInstance.connect(adr.gameMaster1.wallet).submitProposal(proposalsStruct[i].params), ).to.be.revertedWith('Game over'); - let name = `player${i + 1}` as any as keyof AdrSetupResult; await expect( rankifyInstance .connect(adr.gameMaster1.wallet) @@ -1354,25 +1208,44 @@ describe(scriptName, () => { ), ).to.be.revertedWith('Game over'); }); - it('Gave rewards to winners', async () => { - expect(await rankToken.balanceOf(adr.player2.wallet.address, 1)).to.be.equal(2); + it('Gave rewards to winner', async () => { + expect(await rankToken.balanceOf(adr.player2.wallet.address, 1)).to.be.equal(0); expect(await rankToken.balanceOf(adr.player2.wallet.address, 2)).to.be.equal(0); expect(await rankToken.balanceOf(adr.player1.wallet.address, 2)).to.be.equal(1); - expect(await rankToken.balanceOf(adr.player3.wallet.address, 1)).to.be.equal(1); + expect(await rankToken.balanceOf(adr.player3.wallet.address, 1)).to.be.equal(0); expect(await rankToken.balanceOf(adr.player3.wallet.address, 2)).to.be.equal(0); }); it('Allows winner to create game of next rank', async () => { - await expect( - rankifyInstance - .connect(adr.player1.wallet) - ['createGame(address,uint256)'](adr.gameMaster1.wallet.address, 2), - ).to.emit(rankifyInstance, 'gameCreated'); + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 2, + maxPlayerCnt: RInstanceSettings.RInstance_MAX_PLAYERS, + minPlayerCnt: RInstanceSettings.RInstance_MIN_PLAYERS, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + minGameTime: RInstanceSettings.RInstance_MIN_GAME_TIME, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + nTurns: RInstanceSettings.RInstance_MAX_TURNS, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + }; + await expect(rankifyInstance.connect(adr.player1.wallet).createGame(params)).to.emit( + rankifyInstance, + 'gameCreated', + ); }); describe('When game of next rank is created and opened', () => { beforeEach(async () => { - await rankifyInstance - .connect(adr.player1.wallet) - ['createGame(address,uint256)'](adr.gameMaster1.wallet.address, 2); + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 2, + maxPlayerCnt: RInstanceSettings.RInstance_MAX_PLAYERS, + minPlayerCnt: RInstanceSettings.RInstance_MIN_PLAYERS, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + minGameTime: RInstanceSettings.RInstance_MIN_GAME_TIME, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + nTurns: RInstanceSettings.RInstance_MAX_TURNS, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + }; + await rankifyInstance.connect(adr.player1.wallet).createGame(params); await rankifyInstance.connect(adr.player1.wallet).openRegistration(2); }); it('Can be joined only by rank token bearers', async () => { @@ -1392,34 +1265,47 @@ describe(scriptName, () => { }); describe('When a game was played till end', () => { beforeEach(async () => { - const gameCreate = await rankifyInstance - .connect(adr.gameCreator1.wallet) - ['createGame(address,uint256,uint256)'](adr.gameMaster1.wallet.address, 3, 1); - const openRegistration = await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(3); + const state = await rankifyInstance.getContractState(); + await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(state.numGames); await fillParty( getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), rankifyInstance, - 3, + state.numGames, true, true, adr.gameMaster1, ); + console.warn('opened'); await runToTheEnd( - 3, + state.numGames, rankifyInstance, adr.gameMaster1, getPlayers(adr, RInstanceSettings.RInstance_MAX_PLAYERS), ); }); it('Allows players to join another game of same rank if they have rank token', async () => { - const gameCreate = await rankifyInstance - .connect(adr.gameCreator1.wallet) - ['createGame(address,uint256,uint256)'](adr.gameMaster1.wallet.address, 10, 1); + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 2, + maxPlayerCnt: RInstanceSettings.RInstance_MAX_PLAYERS, + minPlayerCnt: RInstanceSettings.RInstance_MIN_PLAYERS, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + minGameTime: RInstanceSettings.RInstance_MIN_GAME_TIME, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + nTurns: RInstanceSettings.RInstance_MAX_TURNS, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + }; + await rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params); + const state = await rankifyInstance.getContractState(); + await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(state.numGames); + await rankifyInstance.connect(adr.player1.wallet).joinGame(state.numGames); const currentT = await time.latest(); await time.setNextBlockTimestamp(currentT + Number(RInstanceSettings.RInstance_TIME_TO_JOIN) + 1); - const openRegistration = await rankifyInstance.connect(adr.gameCreator1.wallet).openRegistration(10); - await rankToken.connect(adr.player1.wallet).setApprovalForAll(rankifyInstance.address, true); - await expect(rankifyInstance.connect(adr.player1.wallet).joinGame(10)).to.emit(rankifyInstance, 'PlayerJoined'); + + await rankToken.connect(adr.player2.wallet).setApprovalForAll(rankifyInstance.address, true); + await expect( + rankifyInstance.connect(adr.player2.wallet).joinGame(state.numGames), + ).to.be.revertedWithCustomError(rankToken, 'insufficient'); }); }); }); @@ -1449,8 +1335,8 @@ describe(scriptName, () => { .balanceOf(adr.player1.wallet.address, 2) .then(balance => balances.push(balance.toNumber())); expect(await rankToken.balanceOf(adr.player1.wallet.address, 2)).to.be.equal(1); - expect(await rankToken.balanceOf(adr.player2.wallet.address, 2)).to.be.equal(1); expect(await rankToken.balanceOf(adr.player3.wallet.address, 2)).to.be.equal(1); + expect(await rankToken.balanceOf(adr.player2.wallet.address, 2)).to.be.equal(1); expect(await rankToken.balanceOf(adr.player4.wallet.address, 2)).to.be.equal(1); expect(await rankToken.balanceOf(adr.player5.wallet.address, 2)).to.be.equal(1); expect(await rankToken.balanceOf(adr.player6.wallet.address, 2)).to.be.equal(1); @@ -1462,7 +1348,7 @@ describe(scriptName, () => { await createGame(rankifyInstance, adr.player1, adr.gameMaster1.wallet.address, 2, true); }); it('Can be joined only by bearers of rank token', async () => { - const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.BestOfState.numGames); + const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.numGames); await rankToken.connect(adr.player2.wallet).setApprovalForAll(rankifyInstance.address, true); await expect(rankifyInstance.connect(adr.player2.wallet).joinGame(lastCreatedGameId)).to.emit( rankifyInstance, @@ -1475,14 +1361,14 @@ describe(scriptName, () => { }); it('Locks rank tokens when player joins', async () => { const balance = await rankToken.balanceOf(adr.player1.wallet.address, 2); - const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.BestOfState.numGames); + const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.numGames); await rankToken.connect(adr.player1.wallet).setApprovalForAll(rankifyInstance.address, true); await rankifyInstance.connect(adr.player1.wallet).joinGame(lastCreatedGameId); const balance2 = await rankToken.balanceOf(adr.player1.wallet.address, 2); expect(await rankToken.unlockedBalanceOf(adr.player1.wallet.address, 2)).to.be.equal(balance.toNumber() - 1); }); it('Returns rank token if player leaves game', async () => { - const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.BestOfState.numGames); + const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.numGames); await rankToken.connect(adr.player1.wallet).setApprovalForAll(rankifyInstance.address, true); await rankifyInstance.connect(adr.player1.wallet).joinGame(lastCreatedGameId); expect(await rankToken.unlockedBalanceOf(adr.player1.wallet.address, 2)).to.be.equal(0); @@ -1490,7 +1376,7 @@ describe(scriptName, () => { expect(await rankToken.unlockedBalanceOf(adr.player1.wallet.address, 2)).to.be.equal(1); }); it('Returns rank token if was game closed', async () => { - const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.BestOfState.numGames); + const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.numGames); await rankToken.connect(adr.player1.wallet).setApprovalForAll(rankifyInstance.address, true); await rankToken.connect(adr.player2.wallet).setApprovalForAll(rankifyInstance.address, true); await rankifyInstance.connect(adr.player1.wallet).joinGame(lastCreatedGameId); @@ -1508,7 +1394,7 @@ describe(scriptName, () => { const balancesBeforeJoined: BigNumber[] = []; beforeEach(async () => { const players = getPlayers(adr, RInstanceSettings.RInstance_MIN_PLAYERS, 0); - const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.BestOfState.numGames); + const lastCreatedGameId = await rankifyInstance.getContractState().then(r => r.numGames); for (let i = 0; i < players.length; i++) { balancesBeforeJoined[i] = await rankToken.unlockedBalanceOf(players[i].wallet.address, 2); } @@ -1539,14 +1425,75 @@ describe(scriptName, () => { balances[i] = await rankToken.balanceOf(players[i].wallet.address, 2); } - expect(balances[0]).to.be.equal(balancesBeforeJoined[0]); - expect(balances[1]).to.be.equal(balancesBeforeJoined[1].add(2)); - expect(balances[2]).to.be.equal(balancesBeforeJoined[2].add(1)); - for (let i = 3; i < players.length; i++) { + expect(balances[0]).to.be.equal(balancesBeforeJoined[0].sub(1)); + for (let i = 1; i < players.length; i++) { expect(balances[i]).to.be.equal(balancesBeforeJoined[i]); } }); }); }); }); + it('should reject game creation with zero minimum time', async () => { + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 1, + maxPlayerCnt: RInstance_MAX_PLAYERS, + minPlayerCnt: RInstance_MIN_PLAYERS, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + nTurns: RInstance_MAX_TURNS, + minGameTime: 0, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + }; + + await env.rankifyToken + .connect(adr.gameCreator1.wallet) + .approve(rankifyInstance.address, ethers.constants.MaxUint256); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).to.be.revertedWith( + 'LibRankify::newGame->Min game time zero', + ); + }); + it('should validate minGameTime is divisible by number of turns', async () => { + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 1, + maxPlayerCnt: RInstance_MAX_PLAYERS, + minPlayerCnt: RInstance_MIN_PLAYERS, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + nTurns: 5, + minGameTime: 3601, // Not divisible by 5 + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + }; + + await env.rankifyToken + .connect(adr.gameCreator1.wallet) + .approve(rankifyInstance.address, ethers.constants.MaxUint256); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).to.be.revertedWithCustomError( + rankifyInstance, + 'NoDivisionReminderAllowed', + ); + }); + + it('should validate turn count is greater than 2', async () => { + const params: IRankifyInstance.NewGameParamsInputStruct = { + gameMaster: adr.gameMaster1.wallet.address, + gameRank: 1, + maxPlayerCnt: RInstance_MAX_PLAYERS, + minPlayerCnt: RInstance_MIN_PLAYERS, + voteCredits: RInstanceSettings.RInstance_VOTE_CREDITS, + nTurns: 2, + minGameTime: 3600, + timePerTurn: RInstanceSettings.RInstance_TIME_PER_TURN, + timeToJoin: RInstanceSettings.RInstance_TIME_TO_JOIN, + }; + + await env.rankifyToken + .connect(adr.gameCreator1.wallet) + .approve(rankifyInstance.address, ethers.constants.MaxUint256); + await expect(rankifyInstance.connect(adr.gameCreator1.wallet).createGame(params)).to.be.revertedWithCustomError( + rankifyInstance, + 'invalidTurnCount', + ); + }); }); diff --git a/test/utils.ts b/test/utils.ts index 38a1221f..aa5ae435 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -1,5 +1,5 @@ // import { time } from "@openzeppelin/test-helpers"; -import hre, { deployments, config } from 'hardhat'; +import hre, { deployments } from 'hardhat'; import aes from 'crypto-js/aes'; import { ethers } from 'hardhat'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; @@ -9,7 +9,6 @@ import { MockERC20, MockERC721, RankToken, - MultipassDiamond, MAODistribution, DAODistributor, ArguableVotingTournament, @@ -19,14 +18,7 @@ import { BigNumber, BigNumberish, BytesLike, Wallet } from 'ethers'; import { assert } from 'console'; import { Deployment } from 'hardhat-deploy/types'; import { HardhatEthersHelpers } from '@nomiclabs/hardhat-ethers/types'; -import { MultipassJs } from '../utils/multipass'; -import { LibMultipass } from '../types/src/facets/DNSFacet'; -import { JsonFragment } from '@ethersproject/abi'; -import fs from 'fs'; -import path from 'path'; -export const MULTIPASS_CONTRACT_NAME = 'MultipassDNS'; -export const MULTIPASS_CONTRACT_VERSION = '0.0.1'; export interface SignerIdentity { name: string; @@ -63,14 +55,11 @@ export interface AdrSetupResult { gameMaster2: SignerIdentity; gameMaster3: SignerIdentity; gameOwner: SignerIdentity; - multipassOwner: SignerIdentity; - registrar1: SignerIdentity; } export interface EnvSetupResult { rankifyToken: Rankify; arguableVotingTournamentDistribution: ArguableVotingTournament; - multipass: MultipassDiamond; rankTokenBase: RankToken; mockERC20: MockERC20; mockERC1155: MockERC1155; @@ -134,7 +123,6 @@ export const setupAddresses = async ( const gameCreator2 = await createRandomIdentityAndSeedEth('gameCreator2'); const gameCreator3 = await createRandomIdentityAndSeedEth('gameCreator3'); const maliciousActor1 = await createRandomIdentityAndSeedEth('maliciousActor'); - const registrar1 = await createRandomIdentityAndSeedEth('registrar1'); const gameMaster1 = await createRandomIdentityAndSeedEth('GM1'); const gameMaster2 = await createRandomIdentityAndSeedEth('GM2'); const gameMaster3 = await createRandomIdentityAndSeedEth('GM3'); @@ -263,21 +251,19 @@ export const setupAddresses = async ( gameCreator1, gameCreator2, gameCreator3, - registrar1, gameMaster1, gameMaster2, gameMaster3, maliciousActor2, maliciousActor3, gameOwner, - multipassOwner: gameOwner, }; }; const baseFee = 1 * 10 ** 18; export const RANKIFY_INSTANCE_CONTRACT_NAME = 'RANKIFY_INSTANCENAME'; export const RANKIFY_INSTANCE_CONTRACT_VERSION = '0.0.1'; -export const RInstance_TIME_PER_TURN = '25'; +export const RInstance_TIME_PER_TURN = 2500; export const RInstance_MAX_PLAYERS = 6; export const RInstance_MIN_PLAYERS = 5; export const RInstance_MAX_TURNS = 3; @@ -298,6 +284,9 @@ export const RInstanceSettings = { RInstance_NUM_WINNERS, RInstance_VOTE_CREDITS, RInstance_SUBJECT, + PRINCIPAL_TIME_CONSTANT: 3600, + RInstance_MIN_GAME_TIME: 3600, + PRINCIPAL_COST: ethers.utils.parseEther('1'), // RInstance_NUM_ACTIONS_TO_TAKE, }; @@ -313,7 +302,7 @@ export const setupTest = deployments.createFixture(async ({ deployments, getName to: owner, value: _eth.utils.parseEther('1'), }); - await deployments.fixture(['MAO', 'multipass']); + await deployments.fixture(['MAO']); const MockERC20F = await _eth.getContractFactory('MockERC20', adr.contractDeployer.wallet); const mockERC20 = (await MockERC20F.deploy('Mock ERC20', 'MCK20', adr.contractDeployer.wallet.address)) as MockERC20; await mockERC20.deployed(); @@ -335,7 +324,6 @@ export const setupTest = deployments.createFixture(async ({ deployments, getName RankifyToken: await deployments.get('Rankify'), RankTokenBase: await deployments.get('RankToken'), // RankifyInstance: await deployments.get('RankifyInstance'), - multipass: await deployments.get('Multipass'), arguableVotingTournamentDistribution: await deployments.get('ArguableVotingTournament'), mockERC20: mockERC20, mockERC721: mockERC721, @@ -435,7 +423,6 @@ export const setupEnvironment = async (setup: { mockERC20: MockERC20; mockERC721: MockERC721; mockERC1155: MockERC1155; - multipass: Deployment; adr: AdrSetupResult; arguableVotingTournamentDistribution: Deployment; }): Promise => { @@ -445,7 +432,6 @@ export const setupEnvironment = async (setup: { // setup.RankifyInstance.abi, // setup.RankifyInstance.address, // )) as RankifyDiamondInstance; - const multipass = (await ethers.getContractAt(setup.multipass.abi, setup.multipass.address)) as MultipassDiamond; const maoDistribution = (await ethers.getContractAt(setup.mao.abi, setup.mao.address)) as MAODistribution; const distributor = (await ethers.getContractAt(setup.distributor.abi, setup.distributor.address)) as DAODistributor; @@ -460,7 +446,6 @@ export const setupEnvironment = async (setup: { distributor, rankifyToken, // rankifyInstance, - multipass, rankTokenBase, mockERC1155: setup.mockERC1155, mockERC20: setup.mockERC20, @@ -513,7 +498,7 @@ export interface ProposalParams { proposer: string; } -export interface ProposalSubmittion { +export interface ProposalSubmission { proposal: string; params: ProposalParams; proposerSignerId: SignerIdentity; @@ -737,7 +722,7 @@ export const mockVotes = async ({ gm: SignerIdentity; verifierAddress: string; players: [SignerIdentity, SignerIdentity, ...SignerIdentity[]]; - distribution: 'ftw' | 'semiUniform' | 'equal'; + distribution: 'ftw' | 'semiUniform' | 'equal' | 'zeros'; }): Promise => { const votes: Array<{ // proof: string; @@ -748,6 +733,8 @@ export const mockVotes = async ({ for (let k = 0; k < players.length; k++) { let creditsLeft = RInstance_VOTE_CREDITS; let playerVote: BigNumberish[] = []; + if(distribution == 'zeros') { + playerVote = players.map(() => 0);} if (distribution == 'ftw') { playerVote = players.map((proposer, idx) => { if (k !== idx) { @@ -807,7 +794,7 @@ export const mockProposalSecrets = async ({ gameId: BigNumberish; turn: BigNumberish; verifierAddress: string; -}): Promise => { +}): Promise => { const _gmW = gm.wallet as Wallet; const proposal = String(gameId) + String(turn) + proposer.id; const encryptedProposal = aes.encrypt(proposal, _gmW.privateKey).toString(); @@ -841,7 +828,7 @@ export const mockProposals = async ({ verifierAddress: string; gm: SignerIdentity; }) => { - let proposals = [] as any as ProposalSubmittion[]; + let proposals = [] as any as ProposalSubmission[]; for (let i = 0; i < players.length; i++) { let proposal = await mockProposalSecrets({ gm, @@ -855,125 +842,9 @@ export const mockProposals = async ({ return proposals; }; -export const signReferralCode = async (message: ReferrerMesage, verifierAddress: string, signer: SignerIdentity) => { - let { chainId } = await ethers.provider.getNetwork(); - - const domain = { - name: MULTIPASS_CONTRACT_NAME, - version: MULTIPASS_CONTRACT_VERSION, - chainId, - verifyingContract: verifierAddress, - }; - - const types = { - proofOfReferrer: [ - { - type: 'address', - name: 'referrerAddress', - }, - ], - }; - const s = await signer.wallet._signTypedData(domain, types, { ...message }); - return s; -}; - -export const getUserRegisterProps = async ( - account: SignerIdentity, - registrar: SignerIdentity, - domainName: string, - deadline: number, - multipassAddress: string, - referrer?: SignerIdentity, - referrerDomain?: string, -) => { - const registrarMessage = { - name: ethers.utils.formatBytes32String(account.name + `.` + domainName), - id: ethers.utils.formatBytes32String(account.id + `.` + domainName), - domainName: ethers.utils.formatBytes32String(domainName), - deadline: ethers.BigNumber.from(deadline), - nonce: ethers.BigNumber.from(0), - }; - - const validSignature = await signRegistrarMessage(registrarMessage, multipassAddress, registrar); - - const applicantData: LibMultipass.RecordStruct = { - name: ethers.utils.formatBytes32String(account.name + `.` + domainName), - id: ethers.utils.formatBytes32String(account.id + `.` + domainName), - wallet: account.wallet.address, - nonce: 0, - domainName: ethers.utils.formatBytes32String(domainName), - }; - - const referrerData: LibMultipass.NameQueryStruct = { - name: ethers.utils.formatBytes32String(referrer?.name ? referrer?.name + `.` + domainName : ''), - domainName: ethers.utils.formatBytes32String(domainName), - id: ethers.utils.formatBytes32String(''), - wallet: ethers.constants.AddressZero, - targetDomain: ethers.utils.formatBytes32String(referrerDomain ?? ''), - }; - let referrerSignature = ethers.constants.HashZero; - const proofOfReferrer: ReferrerMesage = { - referrerAddress: referrer?.wallet.address ?? ethers.constants.AddressZero, - }; - if (referrer?.wallet.address) { - referrerSignature = await signReferralCode(proofOfReferrer, multipassAddress, referrer); - } - - return { - registrarMessage, - validSignature, - applicantData, - referrerData, - referrerSignature, - }; -}; -export const signRegistrarMessage = async ( - message: RegisterMessage, - verifierAddress: string, - signer: SignerIdentity, -) => { - let { chainId } = await ethers.provider.getNetwork(); - - const multipassJs = new MultipassJs({ - chainId: chainId, - contractName: MULTIPASS_CONTRACT_NAME, - version: MULTIPASS_CONTRACT_VERSION, - ...hre.network, - }); - return await multipassJs.signRegistrarMessage(message, verifierAddress, signer.wallet); -}; - -const getSuperInterface = () => { - let mergedArray: JsonFragment[] = []; - function readDirectory(directory: string) { - const files = fs.readdirSync(directory); - - files.forEach(file => { - const fullPath = path.join(directory, file); - if (fs.statSync(fullPath).isDirectory()) { - readDirectory(fullPath); // Recurse into subdirectories - } else if (path.extname(file) === '.json') { - const fileContents = require('../' + fullPath); // Load the JSON file - if (Array.isArray(fileContents)) { - mergedArray = mergedArray.concat(fileContents); // Merge the array from the JSON file - } - } - }); - } - const originalConsoleLog = console.log; - readDirectory('./abi'); - readDirectory('./node_modules/@peeramid-labs/eds/abi'); - console.log = () => {}; // avoid noisy output - const result = new ethers.utils.Interface(mergedArray); - console.log = originalConsoleLog; - return result; -}; - export default { setupAddresses, setupEnvironment, addPlayerNameId, baseFee, - signMessage: signRegistrarMessage, - getSuperInterface, };