diff --git a/.env.example b/.env.example index 5ce00b98..18ad4145 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ export LOCAL_RPC_URL= export ETH_MAINNET_RPC_URL= export ETH_GOERLI_RPC_URL= +export ETH_SEPOLIA_RPC_URL= export ETH_MAINNET_API_KEY= export ETH_GOERLI_API_KEY= export ETHERSCAN_API_KEY= diff --git a/.gitignore b/.gitignore index 74cf7e6a..432e0c78 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ facetsdeployed.txt .vscode/targets.log .vscode/launch.json +.history + bin/ .idea/.gitignore .idea/contracts-next.iml diff --git a/Makefile b/Makefile index fff2c4c8..79fc925d 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,11 @@ gen-i: ## generate solidity interfaces from facet implementations -s "run(string memory, string memory)" src/diamonds/nayms/interfaces/ 0.8.13 \ --ffi -prep-build: ## prepare buld, generate LibGeneratedNaymsFacetHelpers - node ./cli-tools/prep-build.js +prep-build: ## prepare buld, generate LibGeneratedNaymsFacetHelpers. This excludes ACL and Governance facets, which are deployed with the Nayms diamond. + node ./cli-tools/prep-build.js ACL Governance + +prep-build-all: ## prepare buld, generate LibGeneratedNaymsFacetHelpers. This includes all facets in the src/diamonds/nayms/facets folder. + node ./cli-tools/prep-build.js build: ## forge build forge build --names --sizes @@ -164,6 +167,8 @@ initNewDiamond=false facetAction=1 senderAddress=0x2dF0a6dB2F0eF1269bE777C856A7665eeC00649f deploymentSalt=0xdeffffffff +owner=0x931c3aC09202650148Edb2316e97815f904CF4fa +systemAdmin=0x2dF0a6dB2F0eF1269bE777C856A7665eeC00649f schedule-upgrade-goerli: ## schedule upgrade to goerli diamond, then upgrade @forge script SmartDeploy \ @@ -181,7 +186,7 @@ schedule-upgrade-goerli: ## schedule upgrade to goerli diamond, then upgrade deploy: ## smart deploy to goerli @forge script SmartDeploy \ - -s "smartDeploy(bool, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ + -s "smartDeploy(bool, address, address, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${owner} ${systemAdmin} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ -f ${ETH_GOERLI_RPC_URL} \ --chain-id 5 \ --etherscan-api-key ${ETHERSCAN_API_KEY} \ @@ -196,7 +201,7 @@ deploy: ## smart deploy to goerli deploy-mainnet: ## smart deploy to mainnet @forge script SmartDeploy \ - -s "smartDeploy(bool, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ + -s "smartDeploy(bool, address, address, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${owner} ${systemAdmin} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ -f ${ETH_MAINNET_RPC_URL} \ --chain-id 1 \ --etherscan-api-key ${ETHERSCAN_API_KEY} \ @@ -206,12 +211,13 @@ deploy-mainnet: ## smart deploy to mainnet -vv \ --ffi \ --broadcast \ + --slow \ --verify --delay 30 --retries 10 \ ; node cli-tools/postproc-broadcasts.js deploy-mainnet-sim: ## simulate deploy to mainnet @forge script SmartDeploy \ - -s "smartDeploy(bool, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ + -s "smartDeploy(bool, address, address, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${owner} ${systemAdmin} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ -f ${ETH_MAINNET_RPC_URL} \ --chain-id 1 \ --etherscan-api-key ${ETHERSCAN_API_KEY} \ @@ -223,7 +229,7 @@ deploy-mainnet-sim: ## simulate deploy to mainnet deploy-sim: ## simulate smart deploy to goerli forge script SmartDeploy \ - -s "smartDeploy(bool, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ + -s "smartDeploy(bool, address, address, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${owner} ${systemAdmin} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ -f ${ETH_GOERLI_RPC_URL} \ --chain-id 5 \ --etherscan-api-key ${ETHERSCAN_API_KEY} \ @@ -244,7 +250,7 @@ anvil-fork: ## fork goerli locally with anvil anvil-deploy: ## smart deploy locally to anvil forge script SmartDeploy \ - -s "smartDeploy(bool, bool, uint8, string[] memory, bytes32)" true true 0 ${facetsToCutIn} ${deploymentSalt} \ + -s "smartDeploy(bool, address, address, bool, uint8, string[] memory, bytes32)" true ${owner} ${systemAdmin} true 0 ${facetsToCutIn} ${deploymentSalt} \ -f http:\\127.0.0.1:8545 \ --chain-id 31337 \ --sender ${senderAddress} \ @@ -256,7 +262,7 @@ anvil-deploy: ## smart deploy locally to anvil anvil-upgrade: ## smart deploy locally to anvil forge script SmartDeploy \ - -s "smartDeploy(bool, bool, uint8, string[] memory, bytes32)" false false 1 ${facetsToCutIn} ${deploymentSalt}\ + -s "smartDeploy(bool, address, address, bool, uint8, string[] memory, bytes32)" false ${owner} ${systemAdmin} false 1 ${facetsToCutIn} ${deploymentSalt} \ -f http:\\127.0.0.1:8545 \ --chain-id 31337 \ --sender ${senderAddress} \ @@ -356,7 +362,7 @@ slither: ## run slither static analysis upgrade-hash-goerli: ## generate upgrade hash @forge script SmartDeploy \ - -s "hash(bool, uint8, string[] memory, bytes32)" false 1 "[]" ${deploymentSalt} \ + -s "hash(bool, address, address, bool, uint8, string[] memory, bytes32)" false ${owner} ${systemAdmin} ${initNewDiamond} 1 "[]" ${deploymentSalt} \ --fork-url ${ETH_GOERLI_RPC_URL} \ --chain-id 5 \ --etherscan-api-key ${ETHERSCAN_API_KEY} \ @@ -365,12 +371,11 @@ upgrade-hash-goerli: ## generate upgrade hash --mnemonic-indexes 0 \ --ffi \ --silent \ - --json \ - | jq --raw-output '.returns.upgradeHash.value, .returns.cut.value' + && jq --raw-output '.returns.upgradeHash.value, .returns.cut.value' broadcast/SmartDeploy.s.sol/5/dry-run/hash-latest.json upgrade-hash-mainnet: ## generate upgrade hash @forge script SmartDeploy \ - -s "hash(bool, uint8, string[] memory, bytes32)" false 1 "[]" ${deploymentSalt} \ + -s "hash(bool, address, address, bool, uint8, string[] memory, bytes32)" false ${owner} ${systemAdmin} ${initNewDiamond} 1 "[]" ${deploymentSalt} \ --fork-url ${ETH_MAINNET_RPC_URL} \ --chain-id 1 \ --etherscan-api-key ${ETHERSCAN_API_KEY} \ @@ -379,12 +384,11 @@ upgrade-hash-mainnet: ## generate upgrade hash --mnemonic-indexes 0 \ --ffi \ --silent \ - --json \ - | jq --raw-output '.returns.upgradeHash.value, .returns.cut.value' + && jq --raw-output '.returns.upgradeHash.value, .returns.cut.value' broadcast/SmartDeploy.s.sol/1/dry-run/hash-latest.json upgrade-hash-anvil: ## generate upgrade hash forge script SmartDeploy \ - -s "hash(bool, uint8, string[] memory, bytes32)" ${newDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ + -s "hash(bool, address, address, bool, uint8, string[] memory, bytes32)" ${newDiamond} ${owner} ${systemAdmin} ${initNewDiamond} ${facetAction} ${facetsToCutIn} ${deploymentSalt} \ --sender ${senderAddress} \ --mnemonic-paths ./nayms_mnemonic.txt \ --mnemonic-indexes 0 \ diff --git a/cli-tools/prep-build.js b/cli-tools/prep-build.js index 759fc3eb..7552cb28 100755 --- a/cli-tools/prep-build.js +++ b/cli-tools/prep-build.js @@ -1,57 +1,93 @@ #!/usr/bin/env node -const path = require('path') -const fs = require('fs') -const glob = require('glob') -const chalk = require('chalk') +const path = require("path"); +const fs = require("fs"); +const glob = require("glob"); +const chalk = require("chalk"); + +const PROJECT_DIR = path.join(__dirname, ".."); +const FACETS_SRC_DIR = path.join( + PROJECT_DIR, + "src", + "diamonds", + "nayms", + "facets" +); +const INTERFACES_SRC_DIR = path.join( + PROJECT_DIR, + "src", + "diamonds", + "nayms", + "interfaces" +); +const FACETS_DEPLOYED_TXT = path.join(PROJECT_DIR, "facetsdeployed.txt"); +const GENERATED_DEPLOY_NAMES_SOL = path.join( + PROJECT_DIR, + "script", + "utils", + "LibGeneratedNaymsFacetHelpers.sol" +); + +function filterFacetsToExclude(excludeList) { + const allFacets = glob + .sync("*Facet.sol", { cwd: FACETS_SRC_DIR }) + .map((a) => path.basename(a).substring(0, a.indexOf("Facet"))); + return allFacets.filter((facet) => !excludeList.includes(facet)); +} -const PROJECT_DIR = path.join(__dirname, '..') -const FACETS_SRC_DIR = path.join(PROJECT_DIR, 'src', 'diamonds', 'nayms', 'facets') -const INTERFACES_SRC_DIR = path.join(PROJECT_DIR, 'src', 'diamonds', 'nayms', 'interfaces') -const FACETS_DEPLOYED_TXT = path.join(PROJECT_DIR, 'facetsdeployed.txt') -const GENERATED_DEPLOY_NAMES_SOL = path.join(PROJECT_DIR, 'script', 'utils', 'LibGeneratedNaymsFacetHelpers.sol') +// Parse command line arguments +const args = process.argv.slice(2); +const excludedFacets = args.map((arg) => arg.trim()); // load facets -const facetNames = glob.sync('*Facet.sol', { cwd: FACETS_SRC_DIR }).map(a => path.basename(a).substring(0, a.indexOf('Facet'))) +const facetNames = filterFacetsToExclude(excludedFacets); // load interfaces and methods -const facetData = {} -const REGEX = /^\s+function ([A-Za-z0-9_]+)\(/igm -facetNames.forEach(f => { +const facetData = {}; +const REGEX = /^\s+function ([A-Za-z0-9_]+)\(/gim; +facetNames.forEach((f) => { try { - const interfaceName = `I${f}Facet` - const src = fs.readFileSync(path.join(INTERFACES_SRC_DIR, `${interfaceName}.sol`)).toString('utf-8') - const methods = [] - let m + const interfaceName = `I${f}Facet`; + const src = fs + .readFileSync(path.join(INTERFACES_SRC_DIR, `${interfaceName}.sol`)) + .toString("utf-8"); + const methods = []; + let m; while ((m = REGEX.exec(src))) { - methods.push(m[1]) + methods.push(m[1]); } if (!methods.length) { - throw new Error(`Empty interface`) + throw new Error(`Empty interface`); } facetData[f] = { interfaceName, methods, - } + }; } catch (err) { - console.error(chalk.red(`Error loading interface for facet ${f}`)) - throw err + console.error(chalk.red(`Error loading interface for facet ${f}`)); + throw err; } -}) +}); -console.log(chalk.blue('== Facets ==')) +console.log(chalk.blue("== Facets ==")); Object.entries(facetData).forEach(([facetName, data]) => { - console.log(`${facetName} - ${data.methods.length} methods`) -}) -fs.writeFileSync(FACETS_DEPLOYED_TXT, facetNames.join("\n"), { encoding: 'utf-8'}) + console.log(`${facetName} - ${data.methods.length} methods`); +}); +fs.writeFileSync(FACETS_DEPLOYED_TXT, facetNames.join("\n"), { + encoding: "utf-8", +}); // write metadata solidity file -fs.writeFileSync(GENERATED_DEPLOY_NAMES_SOL, ` +fs.writeFileSync( + GENERATED_DEPLOY_NAMES_SOL, + ` // SPDX-License-Identifier: MIT pragma solidity >=0.8.13 <0.9; /// ------------------------------------------------------------------------------------------------------------ /// -/// NOTE: this file is auto-generated by ${path.basename(__filename)}, please DO NOT modify it directly. +/// NOTE: this file is auto-generated by ${path.basename( + __filename + )}, please DO NOT modify it directly. /// /// ------------------------------------------------------------------------------------------------------------ @@ -59,7 +95,12 @@ import "src/diamonds/nayms/INayms.sol"; import { Nayms } from "src/diamonds/nayms/Nayms.sol"; import { InitDiamond } from "src/diamonds/nayms/InitDiamond.sol"; -${facetNames.map((n, i) => `import { ${n}Facet } from "src/diamonds/nayms/facets/${n}Facet.sol";`).join("\n")} +${facetNames + .map( + (n, i) => + `import { ${n}Facet } from "src/diamonds/nayms/facets/${n}Facet.sol";` + ) + .join("\n")} enum NaymsFacetAddressIndex { ${facetNames.join(",\n ")} @@ -77,39 +118,58 @@ library LibGeneratedNaymsFacetHelpers { // yul too slow, so fix stack too deep here { - ${facetNames.map((n, i) => { - const { interfaceName, methods } = facetData[n]; - - const selectors = methods.map((m, mi) => `f${i}[${mi}] = ${interfaceName}.${m}.selector;`) + ${facetNames + .map((n, i) => { + const { interfaceName, methods } = facetData[n]; + + const selectors = methods.map( + (m, mi) => `f${i}[${mi}] = ${interfaceName}.${m}.selector;` + ); - const str1 = `bytes4[] memory f${i} = new bytes4[](${methods.length}); - ${selectors.join("\n ")}` + const str1 = `bytes4[] memory f${i} = new bytes4[](${methods.length}); + ${selectors.join("\n ")}`; - const str2 = `cut[${i}] = IDiamondCut.FacetCut({ + const str2 = `cut[${i}] = IDiamondCut.FacetCut({ facetAddress: address(facetAddresses[uint256(NaymsFacetAddressIndex.${n})]), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f${i} - });` - - return `${str1}\n ${str2}` - }).join("\n ")} + });`; + + return `${str1}\n ${str2}`; + }) + .join("\n ")} } } function deployNaymsFacets() internal returns (address[] memory facetAddresses) { facetAddresses = new address[](${facetNames.length}); - ${facetNames.map(n => `facetAddresses[uint256(NaymsFacetAddressIndex.${n})] = address(new ${n}Facet());`).join("\n ")} + ${facetNames + .map( + (n) => + `facetAddresses[uint256(NaymsFacetAddressIndex.${n})] = address(new ${n}Facet());` + ) + .join("\n ")} } function deployNaymsFacets(NaymsFacetAddressIndex index) internal returns (address facetAddress) { - ${facetNames.map(n => `if (index == NaymsFacetAddressIndex.${n}) { return address(new ${n}Facet()); }`).join("\n ")} + ${facetNames + .map( + (n) => + `if (index == NaymsFacetAddressIndex.${n}) { return address(new ${n}Facet()); }` + ) + .join("\n ")} } function deployNaymsFacetsByName(string memory name) internal returns (address facetAddress) { - if (keccak256(abi.encodePacked(name)) == keccak256(abi.encodePacked("Nayms"))) { return address(new Nayms(msg.sender)); } if (keccak256(abi.encodePacked(name)) == keccak256(abi.encodePacked("InitDiamond"))) { return address(new InitDiamond()); } - ${facetNames.map(n => `if (keccak256(abi.encodePacked(name)) == keccak256(abi.encodePacked("${n}"))) { return address(new ${n}Facet()); }`).join("\n ")} + ${facetNames + .map( + (n) => + `if (keccak256(abi.encodePacked(name)) == keccak256(abi.encodePacked("${n}"))) { return address(new ${n}Facet()); }` + ) + .join("\n ")} } } -`, { encoding: 'utf-8'}) - +`, + { encoding: "utf-8" } +); diff --git a/foundry.toml b/foundry.toml index 67aa6d27..50b502de 100644 --- a/foundry.toml +++ b/foundry.toml @@ -66,11 +66,16 @@ fs_permissions = [ [fuzz] runs = 256 -max_test_rejects = 9999999 +max_test_rejects = 65536 +seed = '0x3e8' +dictionary_weight = 40 +include_storage = true +include_push_bytes = true [rpc_endpoints] mainnet = "${ETH_MAINNET_RPC_URL}" goerli = "${ETH_GOERLI_RPC_URL}" +sepolia = "${ETH_SEPOLIA_RPC_URL}" anvil = "${LOCAL_RPC_URL}" # See more config options https://github.com/foundry-rs/foundry/tree/master/config # 0x00a329c0648769a73afac7f9381e08fb43dbea72 diff --git a/script/deployment/SmartDeploy.s.sol b/script/deployment/SmartDeploy.s.sol index 5a591923..c3dfaee6 100644 --- a/script/deployment/SmartDeploy.s.sol +++ b/script/deployment/SmartDeploy.s.sol @@ -6,6 +6,8 @@ import "../utils/DeploymentHelpers.sol"; contract SmartDeploy is DeploymentHelpers { function smartDeploy( bool deployNewDiamond, + address _owner, + address _systemAdmin, bool initNewDiamond, FacetDeploymentAction facetDeploymentAction, string[] memory facetsToCutIn, @@ -21,7 +23,7 @@ contract SmartDeploy is DeploymentHelpers { { vm.startBroadcast(msg.sender); - (diamondAddress, initDiamondAddress, upgradeHash) = smartDeployment(deployNewDiamond, initNewDiamond, facetDeploymentAction, facetsToCutIn, salt); + (diamondAddress, initDiamondAddress, upgradeHash) = smartDeployment(deployNewDiamond, _owner, _systemAdmin, initNewDiamond, facetDeploymentAction, facetsToCutIn, salt); vm.stopBroadcast(); } @@ -35,26 +37,30 @@ contract SmartDeploy is DeploymentHelpers { string[] memory facetsToCutIn; vm.startBroadcast(msg.sender); IDiamondCut.FacetCut[] memory cut = facetDeploymentAndCut(getDiamondAddressFromFile(), FacetDeploymentAction.UpgradeFacetsWithChangesOnly, facetsToCutIn); - bytes32 upgradeHash = keccak256(abi.encode(cut)); - if (upgradeHash == 0x569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd) { + bytes32 upgradeHash = keccak256(abi.encode(cut, address(0), new bytes(0))); + if (upgradeHash == 0xc597f3eb22d11c46f626cd856bd65e9127b04623d83e442686776a2e3b670bbf) { console2.log("There are no facets to upgrade. This hash is the keccak256 hash of an empty IDiamondCut.FacetCut[]"); } else { nayms.createUpgrade(upgradeHash); nayms.diamondCut(cut, address(0), new bytes(0)); - vm.stopBroadcast(); } + vm.stopBroadcast(); } function hash( bool deployNewDiamond, + address _owner, + address _systemAdmin, + bool initNewDiamond, FacetDeploymentAction facetDeploymentAction, string[] memory facetsToCutIn, bytes32 salt ) external returns (bytes32 upgradeHash, IDiamondCut.FacetCut[] memory cut) { - vm.startPrank(msg.sender); + vm.startBroadcast(msg.sender); - (upgradeHash, cut) = initUpgradeHash(deployNewDiamond, facetDeploymentAction, facetsToCutIn, salt); + address initDiamondAddress; + (upgradeHash, cut, initDiamondAddress) = initUpgradeHash(deployNewDiamond, _owner, _systemAdmin, initNewDiamond, facetDeploymentAction, facetsToCutIn, salt); - vm.stopPrank(); + vm.stopBroadcast(); } } diff --git a/script/utils/DeploymentHelpers.sol b/script/utils/DeploymentHelpers.sol index 695ef7a7..c45f355d 100644 --- a/script/utils/DeploymentHelpers.sol +++ b/script/utils/DeploymentHelpers.sol @@ -23,7 +23,7 @@ contract DeploymentHelpers is Test { using strings for *; using stdStorage for StdStorage; - uint256[] SUPPORTED_CHAINS = [1, 5, 31337]; + uint256[] SUPPORTED_CHAINS = [1, 5, 31337, 11155111]; string public constant artifactsPath = "forge-artifacts/"; // File that is being parsed for the diamond address. If we are deploying a new diamond, then the address will be overwritten here. @@ -91,16 +91,21 @@ contract DeploymentHelpers is Test { // true: deploys a new diamond, writes to deployFile // false: reads deployFile - function diamondDeployment(bool deployNewDiamond, bytes32 salt) public returns (address diamondAddress) { + function diamondDeployment( + bool deployNewDiamond, + address owner, + address systemAdmin, + bytes32 salt + ) public returns (address diamondAddress) { if (deployNewDiamond) { if (salt != 0) { // deterministically deploy diamond Create3Deployer create3 = new Create3Deployer(); console2.log("Deterministic contract address", create3.getDeployed(salt)); - diamondAddress = create3.deployContract(salt, abi.encodePacked(type(Nayms).creationCode, abi.encode(msg.sender)), 0); + diamondAddress = create3.deployContract(salt, abi.encodePacked(type(Nayms).creationCode, abi.encode(owner, systemAdmin)), 0); } else { - diamondAddress = LibGeneratedNaymsFacetHelpers.deployNaymsFacetsByName("Nayms"); + diamondAddress = address(new Nayms(owner, systemAdmin)); } vm.label(address(diamondAddress), "New Nayms Diamond"); @@ -431,6 +436,8 @@ contract DeploymentHelpers is Test { */ function smartDeployment( bool deployNewDiamond, + address _owner, + address _systemAdmin, bool initNewDiamond, FacetDeploymentAction facetDeploymentAction, string[] memory facetsToCutIn, @@ -439,57 +446,80 @@ contract DeploymentHelpers is Test { public returns ( address diamondAddress, - address initDiamond, + address initDiamondAddress, bytes32 upgradeHash ) { // deploys new Nayms diamond, or gets the diamond address from file - diamondAddress = diamondDeployment(deployNewDiamond, salt); + diamondAddress = diamondDeployment(deployNewDiamond, _owner, _systemAdmin, salt); - // todo do we want to deploy a new init contract, or do we want to use the "current" init contract? - if (initNewDiamond) { - // initDiamond = deployContract("InitDiamond"); - initDiamond = LibGeneratedNaymsFacetHelpers.deployNaymsFacetsByName("InitDiamond"); - } // deploys facets IDiamondCut.FacetCut[] memory cut = facetDeploymentAndCut(diamondAddress, facetDeploymentAction, facetsToCutIn); - upgradeHash = keccak256(abi.encode(cut, address(0), "")); - + if (initNewDiamond) { + IInitDiamond initDiamond = IInitDiamond(LibGeneratedNaymsFacetHelpers.deployNaymsFacetsByName("InitDiamond")); + initDiamondAddress = address(initDiamond); + upgradeHash = keccak256(abi.encode(cut, initDiamondAddress, abi.encodeCall(initDiamond.initialize, ()))); + } else { + upgradeHash = keccak256(abi.encode(cut, address(0), "")); + } + console2.log(StdStyle.blue("upgradeHash: "), StdStyle.yellow(vm.toString(upgradeHash))); debugDeployment(diamondAddress, facetsToCutIn, facetDeploymentAction); - cutAndInit(diamondAddress, cut, initDiamond); - - // note todo in [NAY-11], Nayms diamond proxy contract will deploy PhasedDiamondCutFacet instead of DiamondCutFacet at deployment (putting it in the constructor). - // If a new diamond is being deployed, then, following the initialization, we replace the diamondCut() function with the 2-phase diamondCut() function. - if (deployNewDiamond) { - address phasedDiamondCutFacet = address(new PhasedDiamondCutFacet()); - - cut = new IDiamondCut.FacetCut[](1); - bytes4[] memory f0 = new bytes4[](1); - f0[0] = IDiamondCut.diamondCut.selector; - cut[0] = IDiamondCut.FacetCut({ facetAddress: address(phasedDiamondCutFacet), action: IDiamondCut.FacetCutAction.Replace, functionSelectors: f0 }); - - // replace the diamondCut() with the 2-phase diamondCut() - INayms(diamondAddress).diamondCut(cut, address(0), ""); + uint256 upgradeExpiry = INayms(diamondAddress).getUpgrade(upgradeHash); + if (upgradeExpiry >= block.timestamp) { + cutAndInit(diamondAddress, cut, initDiamondAddress); + } else { + console2.log(StdStyle.red("UPGRADE NOT PERFORMED"), StdStyle.blue("(but facets have been deployed and the upgrade hash is valid)")); + console2.log("Upgrade is not scheduled for this hash", vm.toString(upgradeHash)); + console2.log("Upgrade expiry time from getUpgrade()", vm.toString(upgradeExpiry)); + console2.log("Current block.timestamp for chainid", vm.toString(block.chainid), vm.toString(block.timestamp)); + console2.log("Use the upgrade hash given above to schedule an upgrade"); } } function initUpgradeHash( bool deployNewDiamond, + address _owner, + address _systemAdmin, + bool initNewDiamond, FacetDeploymentAction facetDeploymentAction, string[] memory facetsToCutIn, bytes32 salt - ) internal returns (bytes32 upgradeHash, IDiamondCut.FacetCut[] memory cut) { - address diamondAddress = diamondDeployment(deployNewDiamond, salt); + ) + internal + returns ( + bytes32 upgradeHash, + IDiamondCut.FacetCut[] memory cut, + address initDiamondAddress + ) + { + address diamondAddress = diamondDeployment(deployNewDiamond, _owner, _systemAdmin, salt); if (diamondAddress == address(0)) { - return (upgradeHash, cut); + return (upgradeHash, cut, initDiamondAddress); } cut = facetDeploymentAndCut(diamondAddress, facetDeploymentAction, facetsToCutIn); - upgradeHash = keccak256(abi.encode(cut, address(0), "")); + bool initialize; + + try INayms(diamondAddress).isDiamondInitialized() returns (bool result) { + initialize = result; + } catch (bytes memory) { + // if the diamond does not have the isDiamondInitialized method, then check bool initNewDiamond + if (initNewDiamond) { + initialize = false; // if initialize == false then we will deploy InitDiamond and call initialize + } + } + if (!initialize) { + IInitDiamond initDiamond = IInitDiamond(LibGeneratedNaymsFacetHelpers.deployNaymsFacetsByName("InitDiamond")); + initDiamondAddress = address(initDiamond); + upgradeHash = keccak256(abi.encode(cut, initDiamondAddress, abi.encodeCall(initDiamond.initialize, ()))); + } else { + upgradeHash = keccak256(abi.encode(cut, address(0), "")); + } + console2.log(StdStyle.blue("upgradeHash: "), StdStyle.yellow(vm.toString(upgradeHash))); debugDeployment(diamondAddress, facetsToCutIn, facetDeploymentAction); } diff --git a/src/diamonds/nayms/InitDiamond.sol b/src/diamonds/nayms/InitDiamond.sol index 651d15e9..a1d720b0 100644 --- a/src/diamonds/nayms/InitDiamond.sol +++ b/src/diamonds/nayms/InitDiamond.sol @@ -28,7 +28,7 @@ import { IGovernanceFacet } from "../nayms/interfaces/IGovernanceFacet.sol"; error DiamondAlreadyInitialized(); contract InitDiamond { - event InitializeDiamond(address sender, bytes32 systemManager); + event InitializeDiamond(address sender); function initialize() external { AppStorage storage s = LibAppStorage.diamondStorage(); @@ -45,44 +45,9 @@ contract InitDiamond { s.initialChainId = block.chainid; s.initialDomainSeparator = LibEIP712._computeDomainSeparator(); - LibACL._updateRoleGroup(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_ADMINS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_SYSTEM_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_ENTITY_ADMIN, LibConstants.GROUP_ENTITY_ADMINS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_ENTITY_MANAGER, LibConstants.GROUP_ENTITY_MANAGERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_BROKER, LibConstants.GROUP_BROKERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_UNDERWRITERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_INSURED_PARTIES, true); - LibACL._updateRoleGroup(LibConstants.ROLE_CAPITAL_PROVIDER, LibConstants.GROUP_CAPITAL_PROVIDERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_CLAIMS_ADMIN, LibConstants.GROUP_CLAIMS_ADMINS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_TRADER, LibConstants.GROUP_TRADERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_SEGREGATED_ACCOUNT, LibConstants.GROUP_SEGREGATED_ACCOUNTS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_SERVICE_PROVIDER, LibConstants.GROUP_SERVICE_PROVIDERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_BROKER, LibConstants.GROUP_POLICY_HANDLERS, true); - LibACL._updateRoleGroup(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_POLICY_HANDLERS, true); - - LibACL._updateRoleAssigner(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_ADMINS); - LibACL._updateRoleAssigner(LibConstants.ROLE_SYSTEM_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_ENTITY_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_ENTITY_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_BROKER, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_CAPITAL_PROVIDER, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_CLAIMS_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_TRADER, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_SEGREGATED_ACCOUNT, LibConstants.GROUP_SYSTEM_MANAGERS); - LibACL._updateRoleAssigner(LibConstants.ROLE_SERVICE_PROVIDER, LibConstants.GROUP_SYSTEM_MANAGERS); - // disallow creating an object with ID of 0 s.existingObjects[0] = true; - // assign msg.sender as a Nayms System Admin - bytes32 userId = LibHelpers._getIdForAddress(msg.sender); - s.existingObjects[userId] = true; - - LibACL._assignRole(userId, LibAdmin._getSystemId(), LibHelpers._stringToBytes32(LibConstants.ROLE_SYSTEM_ADMIN)); - // Set Commissions (all are in basis points) s.tradingCommissionTotalBP = 30; s.tradingCommissionNaymsLtdBP = 5000; @@ -98,8 +63,6 @@ contract InitDiamond { s.naymsToken = address(this); s.maxDividendDenominations = 1; - s.upgradeExpiration = 7 days; - // adding ERC165 data LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); ds.supportedInterfaces[type(IERC165).interfaceId] = true; @@ -121,6 +84,6 @@ contract InitDiamond { ds.supportedInterfaces[type(IGovernanceFacet).interfaceId] = true; s.diamondInitialized = true; - emit InitializeDiamond(msg.sender, userId); + emit InitializeDiamond(msg.sender); } } diff --git a/src/diamonds/nayms/Nayms.sol b/src/diamonds/nayms/Nayms.sol index 8eaa8c80..027a9e02 100644 --- a/src/diamonds/nayms/Nayms.sol +++ b/src/diamonds/nayms/Nayms.sol @@ -8,14 +8,26 @@ pragma solidity 0.8.17; * Implementation of a diamond. /******************************************************************************/ import { LibDiamond } from "../shared/libs/LibDiamond.sol"; -import { DiamondCutFacet } from "../shared/facets/DiamondCutFacet.sol"; +import { PhasedDiamondCutFacet } from "../shared/facets/PhasedDiamondCutFacet.sol"; import { DiamondLoupeFacet } from "../shared/facets/DiamondLoupeFacet.sol"; import { NaymsOwnershipFacet } from "src/diamonds/shared/facets/NaymsOwnershipFacet.sol"; +import { ACLFacet } from "src/diamonds/nayms/facets/ACLFacet.sol"; +import { GovernanceFacet } from "src/diamonds/nayms/facets/GovernanceFacet.sol"; contract Nayms { - constructor(address _contractOwner) payable { + constructor(address _contractOwner, address _systemAdmin) payable { LibDiamond.setContractOwner(_contractOwner); - LibDiamond.addDiamondFunctions(address(new DiamondCutFacet()), address(new DiamondLoupeFacet()), address(new NaymsOwnershipFacet())); + LibDiamond.setRoleGroupsAndAssigners(); + LibDiamond.setSystemAdmin(_systemAdmin); // note: This method checks to make sure system admin is not the same address as the contract owner. + LibDiamond.setUpgradeExpiration(); + + LibDiamond.addDiamondFunctions( + address(new PhasedDiamondCutFacet()), + address(new DiamondLoupeFacet()), + address(new NaymsOwnershipFacet()), + address(new ACLFacet()), + address(new GovernanceFacet()) + ); } // Find facet for function that is called and execute the diff --git a/src/diamonds/nayms/facets/GovernanceFacet.sol b/src/diamonds/nayms/facets/GovernanceFacet.sol index 107a16a8..980a5f34 100644 --- a/src/diamonds/nayms/facets/GovernanceFacet.sol +++ b/src/diamonds/nayms/facets/GovernanceFacet.sol @@ -10,6 +10,15 @@ contract GovernanceFacet is Modifiers, IGovernanceFacet { event UpdateUpgradeExpiration(uint256 duration); event UpgradeCancelled(bytes32 id, address who); + /** + * @notice Check if the diamond has been initialized. + * @dev This will get the value from AppStorage.diamondInitialized. + */ + function isDiamondInitialized() external view returns (bool) { + AppStorage storage s = LibAppStorage.diamondStorage(); + return s.diamondInitialized; + } + function createUpgrade(bytes32 id) external assertSysAdmin { AppStorage storage s = LibAppStorage.diamondStorage(); @@ -46,4 +55,9 @@ contract GovernanceFacet is Modifiers, IGovernanceFacet { AppStorage storage s = LibAppStorage.diamondStorage(); expiry = s.upgradeScheduled[id]; } + + function getUpgradeExpiration() external view returns (uint256 upgradeExpiration) { + AppStorage storage s = LibAppStorage.diamondStorage(); + upgradeExpiration = s.upgradeExpiration; + } } diff --git a/src/diamonds/nayms/interfaces/CustomErrors.sol b/src/diamonds/nayms/interfaces/CustomErrors.sol index 7b86c2b5..0fc0201f 100644 --- a/src/diamonds/nayms/interfaces/CustomErrors.sol +++ b/src/diamonds/nayms/interfaces/CustomErrors.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +/// @dev The Nayms Diamond (proxy contract) owner (address) must be mutually exclusive with the system admin role. +error OwnerCannotBeSystemAdmin(); + /// @dev Passing in a missing role when trying to assign a role. error RoleIsMissing(); diff --git a/src/diamonds/nayms/interfaces/IGovernanceFacet.sol b/src/diamonds/nayms/interfaces/IGovernanceFacet.sol index e7f33e96..65677011 100644 --- a/src/diamonds/nayms/interfaces/IGovernanceFacet.sol +++ b/src/diamonds/nayms/interfaces/IGovernanceFacet.sol @@ -2,6 +2,12 @@ pragma solidity 0.8.17; interface IGovernanceFacet { + /** + * @notice Check if the diamond has been initialized. + * @dev This will get the value from AppStorage.diamondInitialized. + */ + function isDiamondInitialized() external view returns (bool); + /** * @notice Approve the following upgrade hash: `id` * @dev The diamondCut() has been modified to check if the upgrade has been scheduled. This method needs to be called in order @@ -31,4 +37,10 @@ interface IGovernanceFacet { * @param id This is the keccak256(abi.encode(cut)), where cut is the array of FacetCut struct, IDiamondCut.FacetCut[]. */ function getUpgrade(bytes32 id) external view returns (uint256 expiry); + + /** + * @notice Get the upgrade expiration period. + * @dev This will get the value from AppStorage.upgradeExpiration. AppStorage.upgradeExpiration is added to the block.timestamp to create the upgrade expiration date. + */ + function getUpgradeExpiration() external view returns (uint256 upgradeExpiration); } diff --git a/src/diamonds/nayms/libs/LibACL.sol b/src/diamonds/nayms/libs/LibACL.sol index 93e57b6d..142fad87 100644 --- a/src/diamonds/nayms/libs/LibACL.sol +++ b/src/diamonds/nayms/libs/LibACL.sol @@ -2,11 +2,12 @@ pragma solidity 0.8.17; import { AppStorage, LibAppStorage } from "../AppStorage.sol"; +import { LibDiamond } from "src/diamonds/shared/libs/LibDiamond.sol"; import { LibHelpers } from "./LibHelpers.sol"; import { LibAdmin } from "./LibAdmin.sol"; import { LibObject } from "./LibObject.sol"; import { LibConstants } from "./LibConstants.sol"; -import { RoleIsMissing, AssignerGroupIsMissing } from "src/diamonds/nayms/interfaces/CustomErrors.sol"; +import { OwnerCannotBeSystemAdmin, RoleIsMissing, AssignerGroupIsMissing } from "src/diamonds/nayms/interfaces/CustomErrors.sol"; library LibACL { /** @@ -49,13 +50,18 @@ library LibACL { } } - s.roles[_objectId][_contextId] = _roleId; if (_contextId == LibAdmin._getSystemId() && _roleId == LibHelpers._stringToBytes32(LibConstants.ROLE_SYSTEM_ADMIN)) { - unchecked { - s.sysAdmins += 1; + if (LibDiamond.contractOwner() == LibHelpers._getAddressFromId(_objectId)) { + revert OwnerCannotBeSystemAdmin(); + } else { + unchecked { + s.sysAdmins += 1; + } } } + s.roles[_objectId][_contextId] = _roleId; + emit RoleUpdate(_objectId, _contextId, _roleId, "_assignRole"); } diff --git a/src/diamonds/nayms/libs/LibMarket.sol b/src/diamonds/nayms/libs/LibMarket.sol index f8a91a88..53a93ec6 100644 --- a/src/diamonds/nayms/libs/LibMarket.sol +++ b/src/diamonds/nayms/libs/LibMarket.sol @@ -176,24 +176,14 @@ library LibMarket { uint256 makerBuyAmount = s.offers[bestOfferId].buyAmount; uint256 makerSellAmount = s.offers[bestOfferId].sellAmount; - // Check if best available price on the market is better or same, - // as the one taker is willing to pay, within error margin of ±1. - // This ugly hack is to work around rounding errors. Based on the idea that - // the furthest the amounts can stray from their "true" values is 1. - // Ergo the worst case has `sellAmount` and `makerSellAmount` at +1 away from - // their "correct" values and `makerBuyAmount` and `buyAmount` at -1. - // Since (c - 1) * (d - 1) > (a + 1) * (b + 1) is equivalent to - // c * d > a * b + a + b + c + d - // (For detailed breakdown see https://hiddentao.com/archives/2019/09/08/maker-otc-on-chain-orderbook-deep-dive) + // (For a breakdown on the matching algorithm see https://hiddentao.com/archives/2019/09/08/maker-otc-on-chain-orderbook-deep-dive) if ( - makerBuyAmount * result.remainingBuyAmount > - result.remainingSellAmount * makerSellAmount + makerBuyAmount + result.remainingBuyAmount + result.remainingSellAmount + makerSellAmount + // note: We have removed the "optimistic" matching. + makerBuyAmount * result.remainingBuyAmount > makerSellAmount * result.remainingSellAmount ) { break; // no matching price, bail out } - // ^ The `rounding` parameter is a compromise borne of a couple days of discussion. - // avoid stack-too-deep { // take the offer diff --git a/src/diamonds/nayms/libs/LibTokenizedVault.sol b/src/diamonds/nayms/libs/LibTokenizedVault.sol index 6b18e928..487e4d8b 100644 --- a/src/diamonds/nayms/libs/LibTokenizedVault.sol +++ b/src/diamonds/nayms/libs/LibTokenizedVault.sol @@ -270,7 +270,6 @@ library LibTokenizedVault { uint256 _withdrawnSoFar ) internal pure returns (uint256 _withdrawableDividend) { // The holder dividend is: holderDividend = (totalDividend/tokenSupply) * _amount. The remainer (dust) is lost. - // To get a smaller remainder we re-arrange to: holderDividend = (totalDividend * _amount) / _supply uint256 totalDividendTimesAmount = _totalDividend * _amount; uint256 holderDividend = _supply == 0 ? 0 : (totalDividendTimesAmount / _supply); diff --git a/src/diamonds/shared/libs/LibDiamond.sol b/src/diamonds/shared/libs/LibDiamond.sol index 760a3253..859a50ce 100644 --- a/src/diamonds/shared/libs/LibDiamond.sol +++ b/src/diamonds/shared/libs/LibDiamond.sol @@ -9,6 +9,13 @@ import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; import { IDiamondLoupe } from "../interfaces/IDiamondLoupe.sol"; import { IERC165 } from "../interfaces/IERC165.sol"; import { IERC173 } from "../interfaces/IERC173.sol"; +import { IACLFacet } from "src/diamonds/nayms/interfaces/IACLFacet.sol"; +import { IGovernanceFacet } from "src/diamonds/nayms/interfaces/IGovernanceFacet.sol"; +import { AppStorage, LibAppStorage } from "src/diamonds/nayms/AppStorage.sol"; +import { LibHelpers } from "src/diamonds/nayms/libs/LibHelpers.sol"; +import { LibConstants } from "src/diamonds/nayms/libs/LibConstants.sol"; +import { LibAdmin } from "src/diamonds/nayms/libs/LibAdmin.sol"; +import { LibACL } from "src/diamonds/nayms/libs/LibACL.sol"; library LibDiamond { bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); @@ -30,6 +37,9 @@ library LibDiamond { address contractOwner; } + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event DiamondCut(IDiamondCut.FacetCut[] diamondCut, address init, bytes _calldata); + function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { @@ -37,8 +47,6 @@ library LibDiamond { } } - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; @@ -54,12 +62,60 @@ library LibDiamond { require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } + function setRoleGroupsAndAssigners() internal { + LibACL._updateRoleGroup(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_ADMINS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_SYSTEM_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_ENTITY_ADMIN, LibConstants.GROUP_ENTITY_ADMINS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_ENTITY_MANAGER, LibConstants.GROUP_ENTITY_MANAGERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_BROKER, LibConstants.GROUP_BROKERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_UNDERWRITERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_INSURED_PARTIES, true); + LibACL._updateRoleGroup(LibConstants.ROLE_CAPITAL_PROVIDER, LibConstants.GROUP_CAPITAL_PROVIDERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_CLAIMS_ADMIN, LibConstants.GROUP_CLAIMS_ADMINS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_TRADER, LibConstants.GROUP_TRADERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_SEGREGATED_ACCOUNT, LibConstants.GROUP_SEGREGATED_ACCOUNTS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_SERVICE_PROVIDER, LibConstants.GROUP_SERVICE_PROVIDERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_BROKER, LibConstants.GROUP_POLICY_HANDLERS, true); + LibACL._updateRoleGroup(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_POLICY_HANDLERS, true); + + LibACL._updateRoleAssigner(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_ADMINS); + LibACL._updateRoleAssigner(LibConstants.ROLE_SYSTEM_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_ENTITY_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_ENTITY_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_BROKER, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_CAPITAL_PROVIDER, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_CLAIMS_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_TRADER, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_SEGREGATED_ACCOUNT, LibConstants.GROUP_SYSTEM_MANAGERS); + LibACL._updateRoleAssigner(LibConstants.ROLE_SERVICE_PROVIDER, LibConstants.GROUP_SYSTEM_MANAGERS); + } + + function setSystemAdmin(address _newSystemAdmin) internal { + AppStorage storage s = LibAppStorage.diamondStorage(); + + bytes32 userId = LibHelpers._getIdForAddress(_newSystemAdmin); + s.existingObjects[userId] = true; + + LibACL._assignRole(userId, LibAdmin._getSystemId(), LibHelpers._stringToBytes32(LibConstants.ROLE_SYSTEM_ADMIN)); + } + + function setUpgradeExpiration() internal { + AppStorage storage s = LibAppStorage.diamondStorage(); + /// @dev We set the upgrade expiration to 7 days from now (604800 seconds) + s.upgradeExpiration = 1 weeks; + } + function addDiamondFunctions( address _diamondCutFacet, address _diamondLoupeFacet, - address _ownershipFacet + address _ownershipFacet, + address _aclFacet, + address _governanceFacet ) internal { - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](3); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](5); bytes4[] memory functionSelectors = new bytes4[](1); functionSelectors[0] = IDiamondCut.diamondCut.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: _diamondCutFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); @@ -74,11 +130,29 @@ library LibDiamond { functionSelectors[0] = IERC173.transferOwnership.selector; functionSelectors[1] = IERC173.owner.selector; cut[2] = IDiamondCut.FacetCut({ facetAddress: _ownershipFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); + functionSelectors = new bytes4[](10); + functionSelectors[0] = IACLFacet.assignRole.selector; + functionSelectors[1] = IACLFacet.unassignRole.selector; + functionSelectors[2] = IACLFacet.isInGroup.selector; + functionSelectors[3] = IACLFacet.isParentInGroup.selector; + functionSelectors[4] = IACLFacet.canAssign.selector; + functionSelectors[5] = IACLFacet.getRoleInContext.selector; + functionSelectors[6] = IACLFacet.isRoleInGroup.selector; + functionSelectors[7] = IACLFacet.canGroupAssignRole.selector; + functionSelectors[8] = IACLFacet.updateRoleAssigner.selector; + functionSelectors[9] = IACLFacet.updateRoleGroup.selector; + cut[3] = IDiamondCut.FacetCut({ facetAddress: _aclFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); + functionSelectors = new bytes4[](6); + functionSelectors[0] = IGovernanceFacet.isDiamondInitialized.selector; + functionSelectors[1] = IGovernanceFacet.createUpgrade.selector; + functionSelectors[2] = IGovernanceFacet.updateUpgradeExpiration.selector; + functionSelectors[3] = IGovernanceFacet.cancelUpgrade.selector; + functionSelectors[4] = IGovernanceFacet.getUpgrade.selector; + functionSelectors[5] = IGovernanceFacet.getUpgradeExpiration.selector; + cut[4] = IDiamondCut.FacetCut({ facetAddress: _governanceFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); diamondCut(cut, address(0), ""); } - event DiamondCut(IDiamondCut.FacetCut[] diamondCut, address init, bytes _calldata); - bytes32 internal constant CLEAR_ADDRESS_MASK = bytes32(uint256(0xffffffffffffffffffffffff)); bytes32 internal constant CLEAR_SELECTOR_MASK = bytes32(uint256(0xffffffff << 224)); diff --git a/test/T01Deployment.t.sol b/test/T01Deployment.t.sol index 12718777..88dafb6d 100644 --- a/test/T01Deployment.t.sol +++ b/test/T01Deployment.t.sol @@ -57,6 +57,7 @@ contract T01DeploymentTest is D03ProtocolDefaults { function testInitDiamond() public { InitDiamondFixture fixture = new InitDiamondFixture(); + changePrank(owner); vm.recordLogs(); fixture.initialize(); @@ -65,50 +66,15 @@ contract T01DeploymentTest is D03ProtocolDefaults { Vm.Log[] memory entries = vm.getRecordedLogs(); Vm.Log memory entry = entries[entries.length - 1]; assertEq(entry.topics.length, 1); - assertEq(entry.topics[0], keccak256("InitializeDiamond(address,bytes32)")); - (address a, bytes32 b) = abi.decode(entry.data, (address, bytes32)); - assertEq(a, account0); - assertEq(b, account0Id); + assertEq(entry.topics[0], keccak256("InitializeDiamond(address)")); + address a = abi.decode(entry.data, (address)); + assertEq(a, owner); // check storage - assertEq(fixture.totalSupply(), 100_000_000e18); assertEq(fixture.balanceOf(account0), 100_000_000e18); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_ADMINS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_SYSTEM_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_ENTITY_ADMIN, LibConstants.GROUP_ENTITY_ADMINS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_ENTITY_MANAGER, LibConstants.GROUP_ENTITY_MANAGERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_BROKER, LibConstants.GROUP_BROKERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_UNDERWRITERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_INSURED_PARTIES)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_CAPITAL_PROVIDER, LibConstants.GROUP_CAPITAL_PROVIDERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_CLAIMS_ADMIN, LibConstants.GROUP_CLAIMS_ADMINS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_TRADER, LibConstants.GROUP_TRADERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_SEGREGATED_ACCOUNT, LibConstants.GROUP_SEGREGATED_ACCOUNTS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_SERVICE_PROVIDER, LibConstants.GROUP_SERVICE_PROVIDERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_BROKER, LibConstants.GROUP_POLICY_HANDLERS)); - assertTrue(fixture.isRoleInGroup(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_POLICY_HANDLERS)); - - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_SYSTEM_ADMIN, LibConstants.GROUP_SYSTEM_ADMINS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_SYSTEM_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_ENTITY_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_ENTITY_MANAGER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_BROKER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_CAPITAL_PROVIDER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_BROKER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_INSURED_PARTY, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_UNDERWRITER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_CLAIMS_ADMIN, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_TRADER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_SEGREGATED_ACCOUNT, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.canGroupAssignRole(LibConstants.ROLE_SERVICE_PROVIDER, LibConstants.GROUP_SYSTEM_MANAGERS)); - assertTrue(fixture.isObject(0)); - assertTrue(fixture.isObject(account0Id)); assertEq(fixture.getMaxDividendDenominations(), 1); @@ -135,6 +101,11 @@ contract T01DeploymentTest is D03ProtocolDefaults { // note: Cannot use the InitDiamond contract more than once to initialize a diamond. INayms.FacetCut[] memory cut; + bytes32 upgradeHash = keccak256(abi.encode(cut, address(initDiamond), abi.encodeCall(initDiamond.initialize, ()))); + + changePrank(systemAdmin); + nayms.createUpgrade(upgradeHash); + changePrank(owner); vm.expectRevert(abi.encodePacked(DiamondAlreadyInitialized.selector)); nayms.diamondCut(cut, address(initDiamond), abi.encodeCall(initDiamond.initialize, ())); } diff --git a/test/T01GovernanceUpgrades.t.sol b/test/T01GovernanceUpgrades.t.sol index a542e61c..b25f31bd 100644 --- a/test/T01GovernanceUpgrades.t.sol +++ b/test/T01GovernanceUpgrades.t.sol @@ -28,25 +28,12 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { function setUp() public virtual override { super.setUp(); - // Replace diamondCut() with the two phase diamondCut() - address phasedDiamondCutFacet = address(new PhasedDiamondCutFacet()); + // note: The diamond starts with the PhasedDiamondCutFacet insteaad of the original DiamondCutFacet - IDiamondCut.FacetCut[] memory cut; + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut = new IDiamondCut.FacetCut[](1); - - bytes4[] memory f0 = new bytes4[](1); - f0[0] = IDiamondCut.diamondCut.selector; - - cut[0] = IDiamondCut.FacetCut({ facetAddress: address(phasedDiamondCutFacet), action: IDiamondCut.FacetCutAction.Replace, functionSelectors: f0 }); - - // replace the diamondCut() - nayms.diamondCut(cut, address(0), ""); - - // test out the new diamondCut() testFacetAddress = address(new TestFacet()); - cut = new IDiamondCut.FacetCut[](1); - f0 = new bytes4[](1); + bytes4[] memory f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); @@ -55,8 +42,7 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { } function testUnscheduledGovernanceUpgrade() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; @@ -69,10 +55,8 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { } function testExpiredGovernanceUpgrade() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); @@ -85,17 +69,15 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { } function testGovernanceUpgrade() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); bytes32 upgradeId = keccak256(abi.encode(cut, address(0), "")); nayms.createUpgrade(upgradeId); - + changePrank(owner); nayms.diamondCut(cut, address(0), ""); bytes memory call = abi.encodeCall(TestFacet.sayHello, ()); @@ -105,26 +87,22 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { } function testMustBeOwnerToDoAGovernanceUpgrade() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); bytes32 upgradeId = keccak256(abi.encode(cut, address(0), "")); nayms.createUpgrade(upgradeId); - vm.prank(address(0xAAAAAAAAA)); + changePrank(address(0xAAAAAAAAA)); vm.expectRevert("LibDiamond: Must be contract owner"); nayms.diamondCut(cut, address(0), ""); } function testCancelGovernanceUpgrade() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); @@ -142,10 +120,8 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { } function testScheduleTheSameGovernanceUpgradeBeforeExpiration() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); @@ -162,10 +138,8 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { } function testGovernanceUpgradeMultiple() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); @@ -173,35 +147,35 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { nayms.createUpgrade(upgradeId); // cut in the method sayHello2() - IDiamondCut.FacetCut[] memory cut2; - cut2 = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut2 = new IDiamondCut.FacetCut[](1); bytes4[] memory f1 = new bytes4[](1); - f1 = new bytes4[](1); f1[0] = TestFacet.sayHello2.selector; cut2[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f1 }); bytes32 upgradeId2 = keccak256(abi.encode(cut2, address(0), "")); nayms.createUpgrade(upgradeId2); + changePrank(owner); + nayms.diamondCut(cut, address(0), ""); nayms.diamondCut(cut2, address(0), ""); } function testUpdateUpgradeExpiration() public { - IDiamondCut.FacetCut[] memory cut; - cut = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory f0 = new bytes4[](1); - f0 = new bytes4[](1); f0[0] = TestFacet.sayHello.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f0 }); - nayms.createUpgrade(keccak256(abi.encode(cut))); + bytes32 upgradeId = keccak256(abi.encode(cut, address(0), "")); + nayms.createUpgrade(upgradeId); - vm.prank(address(0xAAAAAAAAA)); + changePrank(address(0xAAAAAAAAA)); vm.expectRevert("not a system admin"); nayms.updateUpgradeExpiration(1 days); - vm.stopPrank(); + + changePrank(systemAdmin); vm.expectRevert("invalid upgrade expiration period"); nayms.updateUpgradeExpiration(59); @@ -210,16 +184,15 @@ contract T01GovernanceUpgrades is D03ProtocolDefaults, MockAccounts { nayms.updateUpgradeExpiration(1 days); - assertEq(block.timestamp + 7 days, nayms.getUpgrade(keccak256(abi.encode(cut)))); + assertEq(block.timestamp + 7 days, nayms.getUpgrade(upgradeId)); - IDiamondCut.FacetCut[] memory cut2; - cut2 = new IDiamondCut.FacetCut[](1); + IDiamondCut.FacetCut[] memory cut2 = new IDiamondCut.FacetCut[](1); bytes4[] memory f1 = new bytes4[](1); - f1 = new bytes4[](1); f1[0] = TestFacet.sayHello2.selector; cut2[0] = IDiamondCut.FacetCut({ facetAddress: address(testFacetAddress), action: IDiamondCut.FacetCutAction.Add, functionSelectors: f1 }); - nayms.createUpgrade(keccak256(abi.encode(cut2))); - assertEq(block.timestamp + 1 days, nayms.getUpgrade(keccak256(abi.encode(cut2)))); + bytes32 upgradeId2 = keccak256(abi.encode(cut2, address(0), "")); + nayms.createUpgrade(upgradeId2); + assertEq(block.timestamp + 1 days, nayms.getUpgrade(upgradeId2)); } } diff --git a/test/T01LibERC20.t.sol b/test/T01LibERC20.t.sol index 5bd9bf8c..a2a22e9c 100644 --- a/test/T01LibERC20.t.sol +++ b/test/T01LibERC20.t.sol @@ -31,7 +31,7 @@ contract T01LibERC20 is D03ProtocolDefaults { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); cut[0] = IDiamondCut.FacetCut({ facetAddress: address(fixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: funcSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); } function getDecimals(address tokenAddress) internal returns (uint8) { @@ -84,9 +84,10 @@ contract T01LibERC20 is D03ProtocolDefaults { assertEq(token.balanceOf(signer1), 100); assertEq(token.balanceOf(account0), 0); - vm.prank(signer1); + changePrank(signer1); token.approve(fixtureAddress, 200); assertEq(token.allowance(signer1, fixtureAddress), 200); + vm.stopPrank(); vm.expectRevert("LibERC20: ERC20 token address has no code"); fixture.transferFrom(address(0), signer1, account0, 1); diff --git a/test/T02ACL.t.sol b/test/T02ACL.t.sol index fd4b3dfc..c7e14657 100644 --- a/test/T02ACL.t.sol +++ b/test/T02ACL.t.sol @@ -15,46 +15,51 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { super.setUp(); } - // the deployer should be in the system admin group at initialization - function testDeployerIsInGroup() public { - assertTrue(nayms.isInGroup(account0Id, systemContext, LibConstants.GROUP_SYSTEM_ADMINS)); + /// deployer, owner, address(this), account0 are all the same address. This address should not be able to have the system admin role + /// systemAdmin is another address + + // the deployer should NOT be a system admin + function testDeployerIsNotASystemAdmin() public { + assertFalse(nayms.isInGroup(account0Id, systemContext, LibConstants.GROUP_SYSTEM_ADMINS)); } function testUnassignLastSystemAdminFails() public { vm.expectRevert("must have at least one system admin"); - nayms.unassignRole(account0Id, systemContext); + nayms.unassignRole(systemAdminId, systemContext); } function testReassignLastSystemAdminFails() public { vm.expectRevert("must have at least one system admin"); - nayms.assignRole(account0Id, systemContext, LibConstants.ROLE_BROKER); + nayms.assignRole(systemAdminId, systemContext, LibConstants.ROLE_BROKER); } function testUnassignSystemAdmin() public { nayms.assignRole(signer1Id, systemContext, LibConstants.ROLE_SYSTEM_ADMIN); - vm.prank(signer1); - nayms.unassignRole(account0Id, systemContext); - vm.stopPrank(); + changePrank(signer1); + nayms.unassignRole(systemAdminId, systemContext); } + /// test_canAssign_SystemAdminCanAssignAnyRoleToAnotherObjectInSystemContext function testDeployerAssignRoleToAnotherObject() public { string memory role = LibConstants.ROLE_ENTITY_ADMIN; // assign the role entity admin to deployer / account0 - assertTrue(nayms.canAssign(account0Id, signer1Id, systemContext, role)); + assertTrue(nayms.canAssign(systemAdminId, signer1Id, systemContext, role)); nayms.assignRole(signer1Id, systemContext, role); // the group that the deployer / account0 is in is now the entity admins group assertTrue(nayms.isInGroup(signer1Id, systemContext, LibConstants.GROUP_ENTITY_ADMINS)); } - function testDeployerAssignAnyRoleToAnotherObjectInNewContext() public { + /// test_canAssign_SystemAdminCanAssignAnyRoleToAnotherObjectInAnyContext + function testSystemAdminAssignAnyRoleToAnotherObjectInNewContext() public { string memory role = LibConstants.ROLE_SYSTEM_MANAGER; bytes32 context = LibHelpers._stringToBytes32("test"); // assign the role signer1 - assertTrue(nayms.canAssign(account0Id, signer1Id, context, role)); + // changePrank(systemAdmin); + assertTrue(nayms.canAssign(systemAdminId, signer1Id, context, role)); nayms.assignRole(signer1Id, context, role); // the group that the signer1 is in is now the approved users group @@ -112,7 +117,7 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { // signer1 tries to assign to signer2 assertFalse(nayms.canAssign(signer1Id, signer2Id, context, role), "signer1 CAN assign role to signer2 when they SHOULDN'T be able to."); - vm.prank(signer1); + changePrank(signer1); vm.expectRevert("not in assigners group"); nayms.assignRole(signer2Id, context, role); } @@ -128,7 +133,7 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { // signer2 tries to assign to signer3 assertTrue(nayms.canAssign(signer2Id, signer3Id, context, role)); - vm.prank(signer2); + changePrank(signer2); nayms.assignRole(signer3Id, context, role); } @@ -141,8 +146,10 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { // signer3 makes signer2 an approved user assertTrue(nayms.canAssign(signer3Id, signer2Id, context, role)); - vm.prank(signer3); + changePrank(signer3); nayms.assignRole(signer2Id, context, role); + + changePrank(systemAdmin); assertTrue(nayms.isInGroup(signer2Id, context, LibConstants.GROUP_SYSTEM_MANAGERS)); } @@ -156,7 +163,7 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { nayms.assignRole(signer1Id, context, role); // signer1 tries to unassign to signer2 - vm.prank(signer1); + changePrank(signer1); vm.expectRevert("not in assigners group"); nayms.unassignRole(signer2Id, context); } @@ -173,7 +180,7 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { nayms.assignRole(entityId1, systemContext, LibConstants.ROLE_SYSTEM_MANAGER); // signer1 tries to unassign to signer2 - vm.prank(signer1); + changePrank(signer1); nayms.unassignRole(signer2Id, context); } @@ -184,12 +191,12 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { testAssignersCanAssignRole(); // signer1 tries to unassign signer2 as approved user - vm.prank(signer1); + changePrank(signer1); vm.expectRevert("not in assigners group"); nayms.unassignRole(signer2Id, context); // signer3 can unassign signer2 as approved user - vm.prank(signer3); + changePrank(signer3); nayms.unassignRole(signer2Id, context); assertFalse(nayms.isInGroup(signer2Id, context, LibConstants.GROUP_SYSTEM_MANAGERS)); } @@ -201,7 +208,7 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { // signer3 makes signer2 an approved user testAssignersCanAssignRole(); - vm.prank(signer3); + changePrank(signer3); vm.recordLogs(); nayms.unassignRole(signer2Id, context); @@ -268,10 +275,9 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { } function testUpdateRoleAssignerFailIfNotAdmin() public { - vm.startPrank(account1); + changePrank(account1); vm.expectRevert("not a system admin"); nayms.updateRoleAssigner("role", "group"); - vm.stopPrank(); } function testUpdateRoleAssigner() public { @@ -297,7 +303,7 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts { } function testUpdateRoleGroupFailIfNotAdmin() public { - vm.startPrank(account1); + changePrank(account1); vm.expectRevert("not a system admin"); nayms.updateRoleGroup("role", "group", false); vm.stopPrank(); diff --git a/test/T02Admin.t.sol b/test/T02Admin.t.sol index c0fde2b0..1697f8b6 100644 --- a/test/T02Admin.t.sol +++ b/test/T02Admin.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import { D03ProtocolDefaults, console2, LibAdmin, LibConstants, LibHelpers } from "./defaults/D03ProtocolDefaults.sol"; +import { Entity } from "src/diamonds/nayms/interfaces/FreeStructs.sol"; import { MockAccounts } from "test/utils/users/MockAccounts.sol"; import { Vm } from "forge-std/Vm.sol"; import { TradingCommissionsBasisPoints, PolicyCommissionsBasisPoints } from "../src/diamonds/nayms/interfaces/FreeStructs.sol"; @@ -27,7 +28,7 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { // Diamond cut this fixture contract into our nayms diamond in order to test against the diamond cut[0] = IDiamondCut.FacetCut({ facetAddress: address(libFeeRouterFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); } function testGetSystemId() public { @@ -39,7 +40,7 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { } function testSetMaxDividendDenominationsFailIfNotAdmin() public { - vm.startPrank(account1); + changePrank(account1); vm.expectRevert("not a system admin"); nayms.setMaxDividendDenominations(100); vm.stopPrank(); @@ -71,7 +72,7 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { } function testAddSupportedExternalTokenFailIfNotAdmin() public { - vm.startPrank(account1); + changePrank(account1); vm.expectRevert("not a system admin"); nayms.addSupportedExternalToken(wethAddress); vm.stopPrank(); @@ -190,11 +191,11 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { }); // must be sys admin - vm.prank(account9); + changePrank(account9); vm.expectRevert("not a system admin"); nayms.setTradingCommissionsBasisPoints(s); - vm.stopPrank(); + changePrank(systemAdmin); // assert happy path nayms.setTradingCommissionsBasisPoints(s); @@ -216,11 +217,11 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { }); // must be sys admin - vm.prank(account9); + changePrank(account9); vm.expectRevert("not a system admin"); nayms.setPolicyCommissionsBasisPoints(s); - vm.stopPrank(); + changePrank(systemAdmin); nayms.setPolicyCommissionsBasisPoints(s); PolicyCommissionsBasisPoints memory result = getPremiumCommissions(); @@ -238,7 +239,7 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { function testOnlySystemAdminCanCallLockAndUnlockFunction(address userAddress) public { bytes32 userId = LibHelpers._getIdForAddress(userAddress); - vm.startPrank(userAddress); + changePrank(userAddress); if (nayms.isInGroup(userId, systemContext, LibConstants.GROUP_SYSTEM_ADMINS)) { nayms.lockFunction(bytes4(0x12345678)); @@ -257,11 +258,11 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { function testLockFunction() public { // must be sys admin - vm.prank(account9); + changePrank(account9); vm.expectRevert("not a system admin"); nayms.lockFunction(bytes4(0x12345678)); - vm.stopPrank(); + changePrank(systemAdmin); // assert happy path nayms.lockFunction(bytes4(0x12345678)); @@ -271,25 +272,29 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts { function testLockFunctionExternalWithdrawFromEntity() public { bytes32 wethId = LibHelpers._getIdForAddress(wethAddress); + Entity memory entityInfo = initEntity(wethId, 5000, 10000, false); + bytes32 systemAdminEntityId = 0xe011000000000000000000000000000000000000000000000000000000000000; + nayms.createEntity(systemAdminEntityId, systemAdminId, entityInfo, bytes32(0)); + // deposit - writeTokenBalance(account0, naymsAddress, wethAddress, 1 ether); + writeTokenBalance(systemAdmin, naymsAddress, wethAddress, 1 ether); nayms.externalDeposit(wethAddress, 1 ether); - assertEq(nayms.internalBalanceOf(DEFAULT_ACCOUNT0_ENTITY_ID, wethId), 1 ether, "entity1 lost internal WETH"); + assertEq(nayms.internalBalanceOf(systemAdminEntityId, wethId), 1 ether, "entity1 lost internal WETH"); assertEq(nayms.internalTokenSupply(wethId), 1 ether); nayms.lockFunction(ITokenizedVaultIOFacet.externalWithdrawFromEntity.selector); vm.expectRevert("function is locked"); - nayms.externalWithdrawFromEntity(DEFAULT_ACCOUNT0_ENTITY_ID, account0, address(weth), 0.5 ether); + nayms.externalWithdrawFromEntity(systemAdminEntityId, systemAdmin, address(weth), 0.5 ether); - assertEq(nayms.internalBalanceOf(DEFAULT_ACCOUNT0_ENTITY_ID, wethId), 1 ether, "balance should stay the same"); + assertEq(nayms.internalBalanceOf(systemAdminEntityId, wethId), 1 ether, "balance should stay the same"); nayms.unlockFunction(ITokenizedVaultIOFacet.externalWithdrawFromEntity.selector); - nayms.externalWithdrawFromEntity(DEFAULT_ACCOUNT0_ENTITY_ID, account0, address(weth), 0.5 ether); + nayms.externalWithdrawFromEntity(systemAdminEntityId, systemAdmin, address(weth), 0.5 ether); - assertEq(nayms.internalBalanceOf(DEFAULT_ACCOUNT0_ENTITY_ID, wethId), 0.5 ether, "half of balance should be withdrawn"); + assertEq(nayms.internalBalanceOf(systemAdminEntityId, wethId), 0.5 ether, "half of balance should be withdrawn"); } bytes4[] internal s_functionSelectors; diff --git a/test/T02User.t.sol b/test/T02User.t.sol index ffda8734..d45fb7eb 100644 --- a/test/T02User.t.sol +++ b/test/T02User.t.sol @@ -21,7 +21,7 @@ contract T02UserTest is D03ProtocolDefaults, MockAccounts { } function testSetEntityFailsIfNotSysAdmin() public { - vm.prank(signer2); + changePrank(signer2); vm.expectRevert("not a system admin"); nayms.setEntity(account0Id, bytes32(0)); } diff --git a/test/T03NaymsOwnership.t.sol b/test/T03NaymsOwnership.t.sol index 40ec6923..25622359 100644 --- a/test/T03NaymsOwnership.t.sol +++ b/test/T03NaymsOwnership.t.sol @@ -14,7 +14,7 @@ contract T03NaymsOwnershipTest is D03ProtocolDefaults, MockAccounts { } function testTransferOwernshipFailsIfNotSysAdmin() public { - vm.prank(signer2); + changePrank(signer2); vm.expectRevert("not a system admin"); nayms.transferOwnership(signer1); } @@ -22,7 +22,7 @@ contract T03NaymsOwnershipTest is D03ProtocolDefaults, MockAccounts { function testTransferOwernshipFailsIfNewOwnerIsSysAdmin() public { nayms.assignRole(signer1Id, systemContext, LibConstants.ROLE_SYSTEM_ADMIN); - vm.prank(signer1); + changePrank(signer1); vm.expectRevert("NEW owner MUST NOT be sys admin"); nayms.transferOwnership(signer1); } @@ -31,7 +31,7 @@ contract T03NaymsOwnershipTest is D03ProtocolDefaults, MockAccounts { nayms.assignRole(signer1Id, systemContext, LibConstants.ROLE_SYSTEM_ADMIN); nayms.assignRole(signer2Id, systemContext, LibConstants.ROLE_SYSTEM_MANAGER); - vm.prank(signer1); + changePrank(signer1); vm.expectRevert("NEW owner MUST NOT be sys manager"); nayms.transferOwnership(signer2); } @@ -39,7 +39,7 @@ contract T03NaymsOwnershipTest is D03ProtocolDefaults, MockAccounts { function testTransferOwernship() public { nayms.assignRole(signer1Id, systemContext, LibConstants.ROLE_SYSTEM_ADMIN); - vm.prank(signer1); + changePrank(signer1); nayms.transferOwnership(signer2); vm.stopPrank(); @@ -60,27 +60,32 @@ contract T03NaymsOwnershipTest is D03ProtocolDefaults, MockAccounts { // note: for this test, assume that the notSysAdmin address is not a system admin vm.assume(!nayms.isInGroup(notSysAdminId, systemContext, LibConstants.GROUP_SYSTEM_ADMINS)); + vm.label(newOwner, "newOwner"); + vm.label(notSysAdmin, "notSysAdmin"); + vm.label(anotherSysAdmin, "anotherSysAdmin"); + // 1. Diamond is deployed, owner is set to msg.sender // 2. Diamond cuts in facets and initializes state, a sys admin is set to msg.sender who must be the owner since diamondCut() can only be called by the owner // Only a system admin can transfer diamond ownership - vm.prank(notSysAdmin); + changePrank(notSysAdmin); vm.expectRevert("not a system admin"); nayms.transferOwnership(newOwner); // Only a system admin can transfer diamond ownership, the new owner isn't a system admin - vm.prank(newOwner); + changePrank(newOwner); vm.expectRevert("not a system admin"); nayms.transferOwnership(newOwner); // System admin can transfer diamond ownership + changePrank(systemAdmin); nayms.transferOwnership(newOwner); assertTrue(nayms.owner() == newOwner); bytes32 anotherSysAdminId = LibHelpers._getIdForAddress(address(anotherSysAdmin)); nayms.assignRole(anotherSysAdminId, systemContext, LibConstants.ROLE_SYSTEM_ADMIN); - vm.prank(anotherSysAdmin); + changePrank(anotherSysAdmin); nayms.transferOwnership(nayms.owner()); } } diff --git a/test/T03SystemFacet.t.sol b/test/T03SystemFacet.t.sol index 2f80cc42..f8047267 100644 --- a/test/T03SystemFacet.t.sol +++ b/test/T03SystemFacet.t.sol @@ -39,7 +39,7 @@ contract T03SystemFacetTest is D03ProtocolDefaults, MockAccounts { bytes32 objectId1 = "0x1"; vm.expectRevert("not a system manager"); - vm.prank(account1); + changePrank(account1); nayms.createEntity(objectId1, objectContext1, initEntity(wethId, 5000, LibConstants.BP_FACTOR, true), "entity test hash"); } diff --git a/test/T03TokenizedVault.t.sol b/test/T03TokenizedVault.t.sol index f0f5d793..ff8d2180 100644 --- a/test/T03TokenizedVault.t.sol +++ b/test/T03TokenizedVault.t.sol @@ -94,7 +94,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { cut[0] = IDiamondCut.FacetCut({ facetAddress: address(tradingCommissionsFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: tcSelectors }); cut[1] = IDiamondCut.FacetCut({ facetAddress: address(tokenizedVaultFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: tvSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); c = getCommissions(); } @@ -147,7 +147,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { uint256 externalDepositAmount = depositAmount / 5; // note: deposits must be an exisiting entity: s.existingEntities[_receiverId] - vm.prank(address(999999)); + changePrank(address(999999)); vm.expectRevert("extDeposit: invalid receiver"); nayms.externalDeposit(wethAddress, 1); @@ -155,17 +155,16 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { nayms.externalDeposit(address(0xBADAAAAAAAAA), 1); // deposit to entity1 - vm.startPrank(address(signer1)); + changePrank(address(signer1)); writeTokenBalance(signer1, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, externalDepositAmount); assertEq(weth.balanceOf(signer1), depositAmount - externalDepositAmount, "signer1 WETH balance after externalDeposit should DECREASE (transfer)"); assertEq(weth.balanceOf(naymsAddress), externalDepositAmount, "nayms WETH balance after externalDeposit should INCREASE (transfer)"); assertEq(nayms.internalBalanceOf(entity1, nWETH), externalDepositAmount, "entity1 nWETH balance should INCREASE (1:1 internal mint)"); assertEq(nayms.internalTokenSupply(nWETH), externalDepositAmount, "nWETH total supply should INCREASE (1:1 internal mint)"); - vm.stopPrank(); // deposit to entity2 - vm.startPrank(address(signer2)); + changePrank(address(signer2)); writeTokenBalance(signer2, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, externalDepositAmount); vm.stopPrank(); @@ -208,7 +207,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { uint256 externalDepositAmount = depositAmount / 5; // note: deposits must be an exisiting entity: s.existingEntities[_receiverId] - vm.startPrank(address(999999)); + changePrank(address(999999)); writeTokenBalance(address(999999), naymsAddress, wethAddress, depositAmount); vm.expectRevert("extDeposit: invalid receiver"); @@ -241,6 +240,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalBalanceOf(account0Id, nWETH), 0, "account0Id nWETH balance should start at 0"); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, depositAmount); // note Depositing to account0's associated entity @@ -264,7 +264,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { uint256 entity1WethInternalBalance = nayms.internalBalanceOf(entity1, nWETH); uint256 naymsWethInternalTokenSupply = nayms.internalTokenSupply(nWETH); - vm.prank(signer1); + changePrank(signer1); nayms.externalWithdrawFromEntity(entity1, account0, wethAddress, 100); assertEq(weth.balanceOf(account0), account0WethBalanceAccount0 + 100, "account0 got WETH"); @@ -281,9 +281,10 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { bytes32 acc9Id = LibHelpers._addressToBytes32(account9); nayms.setEntity(acc9Id, acc0EntityId); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 1 ether); - vm.prank(account9); + changePrank(account9); vm.expectRevert("payDividendFromEntity: not the entity's admin"); nayms.payDividendFromEntity(bytes32("0x1"), 1 ether); } @@ -293,6 +294,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalBalanceOf(acc0EntityId, nWETH), 0, "account0Id nWETH balance should start at 0"); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 1 ether); @@ -309,9 +311,10 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { address nonAdminAddress = vm.addr(0xACC9); bytes32 nonAdminId = LibHelpers._getIdForAddress(nonAdminAddress); + changePrank(systemAdmin); nayms.setEntity(nonAdminId, acc0EntityId); - vm.startPrank(nonAdminAddress); + changePrank(nonAdminAddress); vm.expectRevert("payDividendFromEntity: not the entity's admin"); nayms.payDividendFromEntity(randomGuid, 10 ether); vm.stopPrank(); @@ -333,6 +336,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalBalanceOf(account0Id, nWETH), 0, "acc0EntityId nWETH balance should start at 0"); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, depositAmount); // note Depositing to account0's associated entity @@ -343,6 +347,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { // No withdrawable dividends. assertEq(withdrawableDiv, 0); + changePrank(systemAdmin); // note: starting a token sale which mints participation tokens nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); nayms.startTokenSale(acc0EntityId, 1e18, 1e18); @@ -351,6 +356,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalTokenSupply(acc0EntityId), 1 ether, ""); bytes32 randomGuid = bytes32("0x1"); + changePrank(account0); nayms.payDividendFromEntity(randomGuid, 1 ether); // note:When the participation token supply is non zero, assertEq(nayms.internalBalanceOf(acc0EntityId, nWETH), 0, "acc0EntityId nWETH balance should DECREASE (transfer)"); @@ -364,7 +370,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalBalanceOf(signer1Id, nWETH), 0, ""); - vm.startPrank(signer1); + changePrank(signer1); writeTokenBalance(signer1, naymsAddress, wethAddress, depositAmount); bytes32 signer1EntityId = nayms.getEntity(signer1Id); @@ -390,6 +396,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { ); nayms.internalBalanceOf(signer1Id, nWETH); // no change + changePrank(account0); nayms.withdrawDividend(acc0EntityId, nWETH, nWETH); nayms.withdrawAllDividends(account0Id, nWETH); assertEq(nayms.internalBalanceOf(acc0EntityId, nWETH), 2 ether, "acc0EntityId nWETH balance should STAY THE SAME"); @@ -422,9 +429,11 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { bytes32 eAlice = nayms.getEntity(account0Id); bytes32 eBob = nayms.getEntity(signer1Id); + changePrank(alice); writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); // note: starting a token sale which mints participation tokens + changePrank(systemAdmin); nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); nayms.startTokenSale(eAlice, 1e18, 1e18); @@ -432,11 +441,12 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalTokenSupply(eAlice), 1e18, "eAlice participation token supply should INCREASE (mint)"); assertEq(nayms.internalBalanceOf(eAlice, eAlice), 1e18, "eAlice's eAlice balance should INCREASE (mint)"); - bytes32 randomGuid = bytes32("0x1"); + changePrank(alice); nayms.externalDeposit(wethAddress, 1 ether); assertEq(nayms.internalTokenSupply(nWETH), 1 ether, "nWETH token supply should INCREASE (mint)"); assertEq(nayms.internalBalanceOf(eAlice, nWETH), 1 ether, "eAlice's nWETH balance should INCREASE (deposit)"); + bytes32 randomGuid = bytes32("0x1"); nayms.payDividendFromEntity(randomGuid, 1 ether); // eAlice is paying out a dividend assertEq(nayms.internalBalanceOf(eAlice, nWETH), 1 ether - 1 ether, "eAlice's nWETH balance should DECREASE (transfer to dividend bank)"); @@ -451,7 +461,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { TradingCommissions memory tc = nayms.calculateTradingCommissions(takerBuyAmount); - vm.startPrank(bob); + changePrank(bob); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 1 ether + tc.totalCommissions); assertEq(nayms.internalBalanceOf(eBob, nWETH), 1 ether + tc.totalCommissions, "eBob's nWETH balance should INCREASE"); @@ -478,23 +488,23 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { address charlie = signer2; bytes32 eCharlie = nayms.getEntity(signer2Id); + changePrank(alice); writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 80_000); // to be used for dividend payments - vm.startPrank(bob); + changePrank(bob); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); - vm.stopPrank(); assertEq(nayms.internalBalanceOf(eBob, nWETH), 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); - vm.startPrank(charlie); + changePrank(charlie); writeTokenBalance(charlie, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 17_000 + nayms.calculateTradingCommissions(17_000).totalCommissions); - vm.stopPrank(); // note: starting a token sale which mints participation tokens + changePrank(systemAdmin); nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); nayms.startTokenSale(eAlice, 20_000, 20_000); @@ -502,10 +512,10 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalTokenSupply(eAlice), 20_000, "eAlice participation token supply should INCREASE (mint)"); assertEq(nayms.internalBalanceOf(eAlice, eAlice), 20_000, "eAlice's eAlice balance should INCREASE (mint)"); - vm.prank(bob); + changePrank(bob); nayms.executeLimitOffer(nWETH, 3_000, eAlice, 3_000); // 1:1 purchase price - vm.prank(charlie); + changePrank(charlie); nayms.executeLimitOffer(nWETH, 17_000, eAlice, 17_000); // 1:1 purchase price assertEq(nayms.internalBalanceOf(eBob, eAlice), 3_000); @@ -516,6 +526,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.getWithdrawableDividend(eBob, eAlice, nWETH), 0); assertEq(nayms.getWithdrawableDividend(eCharlie, eAlice, nWETH), 0); + changePrank(alice); nayms.payDividendFromEntity(bytes32("0x1"), 40_000); // eAlice is paying out a dividend assertEq(nayms.internalBalanceOf(eAlice, nWETH), 60_000); assertEq(nayms.internalBalanceOf(dividendBankId, nWETH), 40_000); @@ -549,44 +560,26 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { uint256 bobEAliceBuyAmount, uint256 dividendAmount ) public { - vm.assume(10_000 < bobWethDepositAmount && bobWethDepositAmount <= type(uint128).max - 1); // not inclusive of 2**128 - vm.assume(10_000 < eAliceParTokenSaleAmount && eAliceParTokenSaleAmount <= type(uint128).max - 1); // not inclusive of 2**128 - vm.assume(10_000 < eAliceParTokenPrice && eAliceParTokenPrice <= type(uint128).max - 1); // not inclusive of 2**128 - vm.assume(10_000 < bobEAliceBuyAmount && bobEAliceBuyAmount <= type(uint128).max - 1); // not inclusive of 2**128 - vm.assume(1 < dividendAmount && dividendAmount <= type(uint128).max - 1); // not inclusive of 2**128 - - uint256 uint128Val = type(uint128).max; - bobWethDepositAmount = bound(bobWethDepositAmount, 10_001, uint128Val - 1); - eAliceParTokenSaleAmount = bound(eAliceParTokenSaleAmount, 10_001, uint128Val - 1); - eAliceParTokenPrice = bound(eAliceParTokenPrice, 10_001, uint128Val - 1); - bobEAliceBuyAmount = bound(bobEAliceBuyAmount, 10_001, uint128Val - 1); + dividendAmount = bound(dividendAmount, 1, type(uint128).max); + bobWethDepositAmount = bound(bobWethDepositAmount, 10_000, type(uint128).max); + eAliceParTokenSaleAmount = bound(eAliceParTokenSaleAmount, 10_000, type(uint128).max); + eAliceParTokenPrice = bound(eAliceParTokenPrice, 10_000, type(uint128).max); + bobEAliceBuyAmount = bound(bobEAliceBuyAmount, 10_000, type(uint128).max); require(bobEAliceBuyAmount >= 10_000 && bobEAliceBuyAmount <= type(uint128).max); - // bobWethDepositAmount = 20000; - // eAliceParTokenSaleAmount = 80000000000; - // eAliceParTokenPrice = 20000; - // bobEAliceBuyAmount = 4000000; // minimum buy amount - // bobEAliceBuyAmount = 4000000 - 1; - - // 1 token can afford eAliceParTokenSaleAmount / eAliceParTokenPrice == 4000000.0 eAlice - // 1 eAlice can afford eAliceParTokenPrice / eAliceParTokenSaleAmount == 0.000_000_25 tokens - - console2.log("bobWethDepositAmount", bobWethDepositAmount); - console2.log("eAliceParTokenSaleAmount", eAliceParTokenSaleAmount); - console2.log("eAliceParTokenPrice", eAliceParTokenPrice); - console2.log("bobEAliceBuyAmount", bobEAliceBuyAmount); - + changePrank(alice); writeTokenBalance(alice, naymsAddress, wethAddress, type(uint256).max); // --- Deposit WETH to eAlice --- // nayms.externalDeposit(wethAddress, type(uint256).max); // --- Internal transfer nWETH from eAlice to eBob ---/ - nayms.internalTransferFromEntity(eBob, nWETH, bobWethDepositAmount + nayms.calculateTradingCommissions(bobWethDepositAmount).totalCommissions); + nayms.internalTransferFromEntity(eBob, nWETH, bobWethDepositAmount + eAliceParTokenPrice + nayms.calculateTradingCommissions(eAliceParTokenPrice).totalCommissions); - assertEq(nayms.internalBalanceOf(eBob, nWETH), bobWethDepositAmount + nayms.calculateTradingCommissions(bobWethDepositAmount).totalCommissions); + console2.log("commissions amount", nayms.calculateTradingCommissions(bobWethDepositAmount).totalCommissions); // note: starting a token sale which mints participation tokens + changePrank(systemAdmin); nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); nayms.startTokenSale(eAlice, eAliceParTokenSaleAmount, eAliceParTokenPrice); @@ -594,7 +587,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalTokenSupply(eAlice), eAliceParTokenSaleAmount, "eAlice participation token supply should INCREASE (mint)"); assertEq(nayms.internalBalanceOf(eAlice, eAlice), eAliceParTokenSaleAmount, "eAlice's eAlice balance should INCREASE (mint)"); - vm.prank(bob); + changePrank(bob); // note: Purchase an arbitrary amount of eAlice // note: bob is selling bobWethDepositAmount of nWETH for bobEAliceBuyAmount of eAlice // if the buy amount is less than the price of 1, then the buy amount is calculated to be 0 and the transaction will revert @@ -611,22 +604,24 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { uint256 relativeOfferPrice = bobWethDepositAmount / bobEAliceBuyAmount; if (bobEAliceBuyAmount < relativePriceOfEAlice || (bobEAliceBuyAmount < relativePriceOfEAlice && relativeOfferPrice < relativePriceOfEAlice)) { + assertEq(nayms.internalBalanceOf(eBob, eAlice), 0, "eBob's eAlice balance should STAY THE SAME (executeLimitOffer)"); + // when bob is trying to buy an amount of eAlice that is valued at less than 1 token, the buy amount is calculated to be 0 vm.expectRevert("buy amount must be >0"); nayms.executeLimitOffer(nWETH, bobWethDepositAmount, eAlice, bobEAliceBuyAmount); - - assertEq(nayms.internalBalanceOf(eBob, eAlice), 0, "eBob's eAlice balance should STAY THE SAME (executeLimitOffer)"); } else { nayms.executeLimitOffer(nWETH, bobWethDepositAmount, eAlice, bobEAliceBuyAmount); uint256 balanceOfEbob = nayms.internalBalanceOf(eBob, eAlice); + changePrank(alice); bytes32 randomGuid = bytes32("0x1"); nayms.payDividendFromEntity(randomGuid, dividendAmount); // eAlice is paying out a dividend uint256 calc = (balanceOfEbob * dividendAmount) / eAliceParTokenSaleAmount; assertEq(nayms.getWithdrawableDividend(eBob, eAlice, nWETH), calc); + changePrank(bob); uint256 bobWethBalance = nayms.internalBalanceOf(eBob, nWETH); nayms.withdrawDividend(eBob, eAlice, nWETH); assertEq(nayms.internalBalanceOf(eBob, nWETH), bobWethBalance + calc); @@ -641,45 +636,43 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { address charlie = signer2; bytes32 eCharlie = nayms.getEntity(signer2Id); + changePrank(alice); writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); writeTokenBalance(alice, naymsAddress, wbtcAddress, depositAmount); nayms.externalDeposit(wethAddress, 80_000); // to be used for dividend payments - vm.startPrank(bob); + changePrank(bob); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); - vm.stopPrank(); - vm.startPrank(charlie); + changePrank(charlie); writeTokenBalance(charlie, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 17_000 + nayms.calculateTradingCommissions(17_000).totalCommissions); - vm.stopPrank(); - vm.startPrank(david); + changePrank(david); writeTokenBalance(david, naymsAddress, wbtcAddress, depositAmount); nayms.externalDeposit(wbtcAddress, 80_000); // to be used for dividend payments - vm.stopPrank(); - vm.startPrank(emily); + changePrank(emily); writeTokenBalance(emily, naymsAddress, wbtcAddress, depositAmount); nayms.externalDeposit(wbtcAddress, 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); - vm.stopPrank(); - vm.startPrank(faith); + changePrank(faith); writeTokenBalance(faith, naymsAddress, wbtcAddress, depositAmount); nayms.externalDeposit(wbtcAddress, 17_000 + nayms.calculateTradingCommissions(17_000).totalCommissions); - vm.stopPrank(); assertEq(nayms.internalBalanceOf(eBob, nWETH), 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); assertEq(nayms.internalBalanceOf(eEmily, nWBTC), 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); + changePrank(systemAdmin); // note: starting a token sale which mints participation tokens nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); nayms.enableEntityTokenization(eDavid, "eDavid", "eDavid"); nayms.startTokenSale(eAlice, 20_000, 20_000); nayms.startTokenSale(eDavid, 20_000, 20_000); + vm.stopPrank(); // check token supply of participation token (entity token) assertEq(nayms.internalTokenSupply(eAlice), 20_000, "eAlice participation token supply should INCREASE (mint)"); @@ -709,6 +702,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.getWithdrawableDividend(eBob, eAlice, nWETH), 0); assertEq(nayms.getWithdrawableDividend(eCharlie, eAlice, nWETH), 0); + changePrank(alice); nayms.payDividendFromEntity(bytes32("0x1"), 40_000); // eAlice is paying out a dividend assertEq(nayms.internalBalanceOf(eAlice, nWETH), 60_000); assertEq(nayms.internalBalanceOf(dividendBankId, nWETH), 40_000); @@ -722,6 +716,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.getWithdrawableDividend(eBob, eAlice, nWETH), 15_000); assertEq(nayms.getWithdrawableDividend(eCharlie, eAlice, nWETH), 85_000); + vm.stopPrank(); // eDavid, eEmily, eFaith assertEq(nayms.getWithdrawableDividend(eEmily, eDavid, nWETH), 0); @@ -771,23 +766,23 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { address charlie = signer2; bytes32 eCharlie = nayms.getEntity(signer2Id); + changePrank(alice); writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 80_000); // to be used for dividend payments - vm.startPrank(bob); + changePrank(bob); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); - vm.stopPrank(); - vm.startPrank(charlie); + changePrank(charlie); writeTokenBalance(charlie, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 17_000 + nayms.calculateTradingCommissions(17_000).totalCommissions); - vm.stopPrank(); assertEq(nayms.internalBalanceOf(eBob, nWETH), 3_000 + nayms.calculateTradingCommissions(3_000).totalCommissions); // note: starting a token sale which mints participation tokens + changePrank(systemAdmin); nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); nayms.startTokenSale(eAlice, 20_000, 20_000); @@ -795,10 +790,10 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalTokenSupply(eAlice), 20_000, "eAlice participation token supply should INCREASE (mint)"); assertEq(nayms.internalBalanceOf(eAlice, eAlice), 20_000, "eAlice's eAlice balance should INCREASE (mint)"); - vm.prank(bob); + changePrank(bob); nayms.executeLimitOffer(nWETH, 3_000, eAlice, 3_000); // 1:1 purchase price - vm.prank(charlie); + changePrank(charlie); nayms.executeLimitOffer(nWETH, 17_000, eAlice, 17_000); // 1:1 purchase price assertEq(nayms.internalBalanceOf(eBob, eAlice), 3_000); @@ -809,6 +804,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.getWithdrawableDividend(eBob, eAlice, nWETH), 0); assertEq(nayms.getWithdrawableDividend(eCharlie, eAlice, nWETH), 0); + changePrank(alice); nayms.payDividendFromEntity(bytes32("0x1"), 40_000); // eAlice is paying out a dividend assertEq(nayms.internalBalanceOf(eAlice, nWETH), 60_000); assertEq(nayms.internalBalanceOf(dividendBankId, nWETH), 40_000); @@ -828,21 +824,14 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalBalanceOf(eBob, nWETH), 0); + changePrank(systemAdmin); nayms.internalBurn(eBob, eAlice, 3_000); assertEq(nayms.internalBalanceOf(eBob, nWETH), 15_000); nayms.withdrawAllDividends(eBob, eAlice); - // nayms.withdrawDividend(eBob, eAlice, nWETH); assertEq(nayms.internalBalanceOf(eBob, nWETH), 15_000); assertEq(nayms.internalBalanceOf(dividendBankId, nWETH), 85_000); - - // nayms.withdrawDividend(eCharlie, eAlice, nWETH); - // assertEq(nayms.internalBalanceOf(eBob, nWETH), 15_000); - // assertEq(nayms.internalBalanceOf(eCharlie, nWETH), 85_000); - // assertEq(nayms.internalBalanceOf(dividendBankId, nWETH), 0); - - // weth.balanceOf(bob); } function scopeToDefaults(uint256 _input) internal { @@ -899,6 +888,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { // 2. ---- distribute dividends ---- // fund entity0 to distribute as dividends + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, _dividendAmount * 2); assertEq(nayms.internalBalanceOf(entity0Id, nWETH), 0, "entity0 nWETH balance should start at 0"); nayms.externalDeposit(wethAddress, _dividendAmount); @@ -909,7 +899,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { nayms.payDividendFromEntity(guid, _dividendAmount); // entity1 has no share, thus no withdrawable dividend at this point - vm.startPrank(signer1); + changePrank(signer1); uint256 entity1Div = nayms.getWithdrawableDividend(entity1Id, nWETH, nWETH); assertEq(entity1Div, 0, "Entity 1 has no tokens, so should NOT have dividend to claim"); vm.stopPrank(); @@ -941,14 +931,15 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { vm.stopPrank(); // 5. ---- distribute another round of dividends ---- - + changePrank(account0); + console2.log(nayms.internalBalanceOf(entity0Id, nWETH)); bytes32 guid2 = bytes32("0xbEEf"); nayms.payDividendFromEntity(guid2, _dividendAmount); // 6. ---- SHOULD have more withdrawable dividends now! ---- uint256 expectedDividend = (_dividendAmount * takeAmount) / _parTokenSupply; - vm.startPrank(signer1); + changePrank(signer1); uint256 entity1DivAfter2Purchase = nayms.getWithdrawableDividend(entity1Id, entity0Id, nWETH); // tolerate rounding errors @@ -968,50 +959,51 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { address bob = signer1; bytes32 eAlice = nayms.getEntity(account0Id); bytes32 eBob = nayms.getEntity(signer1Id); - writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); + changePrank(alice); + writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); // STAGE 1: Alice is starting an eAlice token sale. + changePrank(systemAdmin); uint256 tokenAmount = 1e18; nayms.startTokenSale(eAlice, tokenAmount, tokenAmount); + changePrank(alice); nayms.externalDeposit(wethAddress, 1 ether); assertEq(nayms.internalBalanceOf(eAlice, nWETH), 1 ether, "eAlice's nWETH balance should INCREASE"); // eAlice is paying out a dividend with guid 0x1 nayms.payDividendFromEntity("0x1", 1 ether); // STAGE 2: Bob is trying to buy all of the newly sold eAlice Tokens. - vm.startPrank(bob); + changePrank(bob); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); TradingCommissions memory tc = nayms.calculateTradingCommissions(tokenAmount); nayms.externalDeposit(wethAddress, 1 ether + tc.totalCommissions); assertEq(nayms.internalBalanceOf(eBob, nWETH), 1 ether + tc.totalCommissions, "eBob's nWETH balance should INCREASE"); nayms.executeLimitOffer(nWETH, 1 ether, eAlice, tokenAmount); - vm.stopPrank(); // STAGE 3: Bob selling the newly purchased eAlice token back to Alice. - vm.startPrank(bob); nayms.executeLimitOffer(eAlice, tokenAmount, nWETH, 1 ether); - vm.stopPrank(); TradingCommissions memory tc2 = nayms.calculateTradingCommissions(tokenAmount); + changePrank(alice); writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 1 ether + tc2.totalCommissions); nayms.executeLimitOffer(nWETH, 1 ether, eAlice, tokenAmount); // STAGE 4: Alice selling the newly purchased eAlice token back to Bob. nayms.executeLimitOffer(eAlice, tokenAmount, nWETH, 1 ether); - vm.startPrank(bob); + changePrank(bob); TradingCommissions memory tc3 = nayms.calculateTradingCommissions(tokenAmount); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); nayms.externalDeposit(wethAddress, 1 ether + tc3.totalCommissions); nayms.executeLimitOffer(nWETH, 1 ether, eAlice, tokenAmount); - vm.stopPrank(); // STAGE 5: Alice wants to pay a dividend to the eAlice token holders. + changePrank(alice); nayms.payDividendFromEntity("0x2", 1 ether); // eAlice is paying out a dividend with new guid "0x2" // Note that up to this point, Bob has not received any dividend because the initial dividend is already all taken by Alice. // STAGE 6: Bob tries to get this new dividend since he now has all the eAlice - vm.prank(bob); + changePrank(bob); assertEq(nayms.internalBalanceOf(eBob, nWETH), 1 ether, "eBob's current balance"); nayms.withdrawDividend(eBob, eAlice, nWETH); // This SHOULD NOT fail because nayms.getWithdrawableDividend(eBob, eAlice, nWETH) will return 0, so Bob will not receive the new dividend. @@ -1025,14 +1017,16 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { address bob = signer1; bytes32 eAlice = nayms.getEntity(account0Id); bytes32 eBob = nayms.getEntity(signer1Id); - writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); nayms.enableEntityTokenization(eAlice, "eAlice", "eAlice"); + changePrank(alice); + writeTokenBalance(alice, naymsAddress, wethAddress, depositAmount); // 1. Alice starts with 500 WETH in its internal balance nayms.externalDeposit(wethAddress, eAliceStartAmount); assertEq(nayms.internalBalanceOf(eAlice, nWETH), eAliceStartAmount, "eAlice's nWETH balance should INCREASE"); // 2. Alice starts a sale, selling 100 ALICE tokens for 100 WETH; + changePrank(systemAdmin); uint256 tokenAmount = 100e18; nayms.startTokenSale(eAlice, tokenAmount, tokenAmount); @@ -1040,6 +1034,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.internalBalanceOf(eAlice, eAlice), tokenAmount, "eAlice's nWETH balance should INCREASE"); // 4. Alice pays 100 WETH as a dividend; + changePrank(alice); nayms.payDividendFromEntity("0x1", 100 ether); // 5. Alice pays 100 WETH as a dividend; @@ -1048,7 +1043,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { // 6. Bob buys all 100 ALICE from Alice. Here, during the transfer, // Alice would have withdrawn the 200 WETH dividend owed to her, // so her balance is 600 WETH (300 + 200 for dividend + 100 from Bob's purchase); - vm.startPrank(bob); + changePrank(bob); writeTokenBalance(bob, naymsAddress, wethAddress, depositAmount); TradingCommissions memory tc = nayms.calculateTradingCommissions(tokenAmount); nayms.externalDeposit(wethAddress, tokenAmount + tc.totalCommissions); @@ -1056,16 +1051,14 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts { nayms.executeLimitOffer(nWETH, tokenAmount, eAlice, tokenAmount); assertEq(nayms.internalBalanceOf(eBob, eAlice), tokenAmount, "eBob's eAlice balance should INCREASE"); assertEq(nayms.internalBalanceOf(eAlice, nWETH), eAliceStartAmount + tokenAmount, "eAlice's nWETH balance should INCREASE"); - vm.stopPrank(); // 7. Bob transfers all 100 ALICE back to Alice; - vm.startPrank(bob); nayms.internalTransferFromEntity(eAlice, eAlice, tokenAmount); assertEq(nayms.internalBalanceOf(eAlice, eAlice), tokenAmount, "eAlice's eAlice balance should INCREASE"); assertEq(nayms.internalBalanceOf(eBob, eAlice), 0, "eAlice's eAlice balance should INCREASE"); - vm.stopPrank(); // 8. Alice pays 500 WETH as a dividend; + changePrank(alice); nayms.payDividendFromEntity("0x3", eAliceStartAmount); // 9. Alice tries to withdraw the 500 WETH dividend, should withdraw all 500 WETH diff --git a/test/T04Entity.t.sol b/test/T04Entity.t.sol index b8ca3f39..56500f84 100644 --- a/test/T04Entity.t.sol +++ b/test/T04Entity.t.sol @@ -45,7 +45,7 @@ contract T04EntityTest is D03ProtocolDefaults { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); cut[0] = IDiamondCut.FacetCut({ facetAddress: address(simplePolicyFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: funcSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); } function getSimplePolicy(bytes32 _policyId) internal returns (SimplePolicy memory) { @@ -69,17 +69,26 @@ contract T04EntityTest is D03ProtocolDefaults { // fund the entity balance uint256 amount = 21000; - weth.approve(naymsAddress, amount); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, amount); assertEq(weth.balanceOf(account0), amount); nayms.externalDeposit(wethAddress, amount); assertEq(nayms.internalBalanceOf(entityId1, wethId), amount); + changePrank(systemAdmin); } function testDomainSeparator() public { bytes32 domainSeparator = nayms.domainSeparatorV4(); - - bytes32 expected = bytes32(0x38c40ddfc309275c926499b83dd3de3a9c824318ef5204fd7ae58f823f845291); + // bytes32 expected = bytes32(0x38c40ddfc309275c926499b83dd3de3a9c824318ef5204fd7ae58f823f845291); + bytes32 expected = keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes("Nayms")), + keccak256("1"), + block.chainid, + naymsAddress + ) + ); assertEq(domainSeparator, expected); // change chain id @@ -187,13 +196,14 @@ contract T04EntityTest is D03ProtocolDefaults { // fund the entity balance uint256 amount = 5_000; - weth.approve(naymsAddress, amount); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, amount); nayms.externalDeposit(wethAddress, amount); assertEq(nayms.internalBalanceOf(entityId1, wethId), amount); assertEq(nayms.getLockedBalance(entityId1, wethId), 0, "NO FUNDS should be locked"); + changePrank(systemAdmin); nayms.createSimplePolicy(policyId1, entityId1, stakeholders, simplePolicy, "test"); uint256 expectedLockedBalance = (simplePolicy.limit * 5_000) / LibConstants.BP_FACTOR; assertEq(nayms.getLockedBalance(entityId1, wethId), expectedLockedBalance, "funds SHOULD BE locked"); @@ -250,7 +260,7 @@ contract T04EntityTest is D03ProtocolDefaults { nayms.createEntity(eAlice, aliceId, entity, "entity test hash"); nayms.createEntity(eBob, bobId, entity, "entity test hash"); - weth.approve(naymsAddress, 10000); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 100000); nayms.externalDeposit(wethAddress, 100000); @@ -270,6 +280,7 @@ contract T04EntityTest is D03ProtocolDefaults { Stakeholders memory stakeholders = Stakeholders(roles, entityIds, signatures); + changePrank(systemAdmin); vm.expectRevert(abi.encodeWithSelector(DuplicateSignerCreatingSimplePolicy.selector, alice, bob)); nayms.createSimplePolicy(policyId1, entityId1, stakeholders, simplePolicy, testPolicyDataHash); } @@ -293,7 +304,7 @@ contract T04EntityTest is D03ProtocolDefaults { nayms.createEntity(eAlice, aliceId, entity, "entity test hash"); nayms.createEntity(eBob, bobId, entity, "entity test hash"); - weth.approve(naymsAddress, 10000); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 100000); nayms.externalDeposit(wethAddress, 100000); @@ -313,6 +324,7 @@ contract T04EntityTest is D03ProtocolDefaults { Stakeholders memory stakeholders = Stakeholders(roles, entityIds, signatures); + changePrank(systemAdmin); nayms.createSimplePolicy(policyId1, entityId1, stakeholders, simplePolicy, testPolicyDataHash); } @@ -357,10 +369,10 @@ contract T04EntityTest is D03ProtocolDefaults { // test caller is system manager vm.expectRevert("not a system manager"); - vm.prank(account9); + changePrank(account9); nayms.createSimplePolicy(policyId1, entityId1, stakeholders, simplePolicy, testPolicyDataHash); - vm.stopPrank(); + changePrank(systemAdmin); // test capacity vm.expectRevert("not enough available capacity"); nayms.createSimplePolicy(policyId1, entityId1, stakeholders, simplePolicy, testPolicyDataHash); @@ -374,12 +386,14 @@ contract T04EntityTest is D03ProtocolDefaults { // fund the policy sponsor entity nayms.updateEntity(entityId1, initEntity(wethId, 5000, 300000, true)); - weth.approve(naymsAddress, 10000); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 100000); assertEq(weth.balanceOf(account0), 100000); nayms.externalDeposit(wethAddress, 100000); assertEq(nayms.internalBalanceOf(entityId1, wethId), 100000); + changePrank(systemAdmin); + // start date too early vm.warp(1); simplePolicy.startDate = block.timestamp - 1; @@ -562,10 +576,11 @@ contract T04EntityTest is D03ProtocolDefaults { // fund the entity balance uint256 amount = 21000; - weth.approve(naymsAddress, amount); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, amount); nayms.externalDeposit(wethAddress, amount); + changePrank(systemAdmin); // create policyId1 with limit of 21000 (stakeholders, simplePolicy) = initPolicyWithLimit(testPolicyDataHash, 21000); nayms.createSimplePolicy(policyId1, entityId1, stakeholders, simplePolicy, testPolicyDataHash); @@ -584,10 +599,12 @@ contract T04EntityTest is D03ProtocolDefaults { newEInfo.maxCapacity = 221_000; nayms.updateEntity(entityId1, newEInfo); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 200_000); nayms.externalDeposit(wethAddress, 200_000); assertEq(nayms.internalBalanceOf(entityId1, simplePolicy.asset), 20998 + 200_000, "after deposit, entity balance of nWETH should INCREASE"); + changePrank(systemAdmin); bytes32 policyId2 = LibHelpers._stringToBytes32("policyId2"); (stakeholders, simplePolicy) = initPolicyWithLimit(testPolicyDataHash, 200_001); @@ -607,6 +624,7 @@ contract T04EntityTest is D03ProtocolDefaults { assertEq(nayms.getLockedBalance(entityId1, simplePolicy.asset), 200_000 - 3, "after cancelling policy, the locked balance should DECREASE"); + changePrank(account0); vm.expectRevert("_internalBurn: insufficient balance available, funds locked"); nayms.externalWithdrawFromEntity(entityId1, account0, wethAddress, 21_000); @@ -628,15 +646,18 @@ contract T04EntityTest is D03ProtocolDefaults { vm.expectRevert("_internalTransfer: insufficient balance available, funds locked"); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId"), policyId1, DEFAULT_INSURED_PARTY_ENTITY_ID, 2); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 1); nayms.externalDeposit(wethAddress, 1); assertEq(nayms.internalBalanceOf(entityId1, simplePolicy.asset), 21001, "entity balance of nWETH should INCREASE by deposit amount"); + changePrank(systemAdmin); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId"), policyId1, DEFAULT_INSURED_PARTY_ENTITY_ID, 2); // claiming 2 assertEq(nayms.internalBalanceOf(entityId1, simplePolicy.asset), 20999, "entity balance of nWETH should DECREASE by pay claim amount"); assertEq(nayms.getEntityInfo(entityId1).utilizedCapacity, 21000 - 1, "entity utilization should DECREASE when a claim is made"); assertEq(nayms.getLockedBalance(entityId1, simplePolicy.asset), 21000 - 1, "entity locked balance should DECREASE"); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 200_000); nayms.externalDeposit(wethAddress, 200_000); assertEq(nayms.internalBalanceOf(entityId1, simplePolicy.asset), 20999 + 200_000, "after deposit, entity balance of nWETH should INCREASE"); @@ -644,6 +665,7 @@ contract T04EntityTest is D03ProtocolDefaults { // increase max cap from 30_000 to 221_000 Entity memory newEInfo = nayms.getEntityInfo(entityId1); newEInfo.maxCapacity = 221_000; + changePrank(systemAdmin); nayms.updateEntity(entityId1, newEInfo); bytes32 policyId2 = LibHelpers._stringToBytes32("policyId2"); @@ -667,17 +689,21 @@ contract T04EntityTest is D03ProtocolDefaults { assertEq(nayms.getLockedBalance(entityId1, simplePolicy.asset), 21000 - 1 + 200_000 - 20999, "after cancelling policy, the locked balance should DECREASE"); + changePrank(account0); vm.expectRevert("_internalBurn: insufficient balance available, funds locked"); nayms.externalWithdrawFromEntity(entityId1, account0, wethAddress, 21_000); nayms.externalWithdrawFromEntity(entityId1, account0, wethAddress, 21_000 - 1); + changePrank(systemAdmin); vm.expectRevert("_internalTransfer: insufficient balance available, funds locked"); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId2"), policyId2, DEFAULT_INSURED_PARTY_ENTITY_ID, 1); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 1); nayms.externalDeposit(wethAddress, 1); + changePrank(systemAdmin); vm.expectRevert("_internalTransfer: insufficient balance available, funds locked"); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId2"), policyId2, DEFAULT_INSURED_PARTY_ENTITY_ID, 3); @@ -699,9 +725,12 @@ contract T04EntityTest is D03ProtocolDefaults { vm.expectRevert("_internalTransfer: insufficient balance available, funds locked"); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId"), policyId1, DEFAULT_INSURED_PARTY_ENTITY_ID, 21000); + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, 20000); nayms.externalDeposit(wethAddress, 20000); + changePrank(systemAdmin); + nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId"), policyId1, DEFAULT_INSURED_PARTY_ENTITY_ID, 21000); assertEq(nayms.getLockedBalance(entityId1, simplePolicy.asset), 10500, "locked balance should DECREASE by half"); @@ -725,7 +754,7 @@ contract T04EntityTest is D03ProtocolDefaults { vm.expectRevert("not a policy handler"); nayms.paySimplePremium(policyId1, 1000); - vm.startPrank(signer2); + changePrank(signer2); simplePolicy.cancelled = true; updateSimplePolicy(policyId1, simplePolicy); @@ -768,6 +797,8 @@ contract T04EntityTest is D03ProtocolDefaults { assertEq(nayms.internalBalanceOf(DEFAULT_INSURED_PARTY_ENTITY_ID, wethId), balanceBeforePremium - premiumAmount); } + changePrank(systemAdmin); + simplePolicy.cancelled = true; updateSimplePolicy(policyId1, simplePolicy); vm.expectRevert("Policy is cancelled"); @@ -776,10 +807,11 @@ contract T04EntityTest is D03ProtocolDefaults { simplePolicy.cancelled = false; updateSimplePolicy(policyId1, simplePolicy); - vm.prank(account9); + changePrank(account9); vm.expectRevert("not a system manager"); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId"), policyId1, DEFAULT_INSURED_PARTY_ENTITY_ID, 10000); - vm.stopPrank(); + + changePrank(systemAdmin); vm.expectRevert("not an insured party"); nayms.paySimpleClaim(LibHelpers._stringToBytes32("claimId"), policyId1, 0, 10000); @@ -825,11 +857,12 @@ contract T04EntityTest is D03ProtocolDefaults { nayms.enableEntityTokenization(entityId1, "e1token", "e1token"); - vm.prank(account9); + changePrank(account9); vm.expectRevert("not a system manager"); nayms.startTokenSale(entityId1, sellAmount, sellAtPrice); vm.stopPrank(); + changePrank(systemAdmin); vm.expectRevert("mint amount must be > 0"); nayms.startTokenSale(entityId1, 0, sellAtPrice); @@ -893,7 +926,7 @@ contract T04EntityTest is D03ProtocolDefaults { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); cut[0] = IDiamondCut.FacetCut({ facetAddress: address(libFeeRouterFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); getReadyToCreatePolicies(); diff --git a/test/T04Market.t.sol b/test/T04Market.t.sol index 16d1d9e8..b7179bcd 100644 --- a/test/T04Market.t.sol +++ b/test/T04Market.t.sol @@ -92,7 +92,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); cut[0] = IDiamondCut.FacetCut({ facetAddress: address(tradingCommissionsFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); c = getCommissions(); } @@ -107,6 +107,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { nayms.createEntity(entity1, signer1Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "entity test hash"); // mint weth for account0 + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, dt.entity1StartingBal); // note: when using writeTokenBalance, this does not update the total supply! @@ -116,11 +117,11 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { // deposit into nayms vaults // note: the entity creator can deposit funds into an entity - vm.startPrank(signer1); + changePrank(signer1); writeTokenBalance(signer1, naymsAddress, wethAddress, dt.entity1StartingBal); nayms.externalDeposit(wethAddress, dt.entity1ExternalDepositAmt); - vm.stopPrank(); + changePrank(systemAdmin); nayms.enableEntityTokenization(entity1, "e1token", "e1token"); // start a token sale: sell entity tokens for nWETH @@ -195,12 +196,14 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { assertEq(nayms.getLockedBalance(entity1, entity1), dt.entity1MintAndSaleAmt, "entity1 nEntity1 balance of tokens for sale should INCREASE (lock)"); // try transfering nEntity1 from entity1 to entity0 - this should REVERT! - vm.startPrank(signer1); + changePrank(signer1); vm.expectRevert("_internalTransfer: insufficient balance available, funds locked"); nayms.internalTransferFromEntity(DEFAULT_ACCOUNT0_ENTITY_ID, entity1, 1); - vm.stopPrank(); assertTrue(nayms.isActiveOffer(1), "Token sale offer should be active"); + + // Change signer back to system admin for the other tests that call this test first + changePrank(systemAdmin); } function testCommissionsPayed() public { @@ -209,19 +212,19 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { // init and fund taker entity nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test"); nayms.createEntity(entity3, signer3Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test"); - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, dt.entity2ExternalDepositAmt); nayms.externalDeposit(wethAddress, dt.entity2ExternalDepositAmt); vm.stopPrank(); - vm.startPrank(signer3); + changePrank(signer3); writeTokenBalance(signer3, naymsAddress, wethAddress, dt.entity3ExternalDepositAmt); nayms.externalDeposit(wethAddress, dt.entity3ExternalDepositAmt); vm.stopPrank(); uint256 naymsBalanceBeforeTrade = nayms.internalBalanceOf(LibHelpers._stringToBytes32(LibConstants.NAYMS_LTD_IDENTIFIER), wethId); - vm.startPrank(signer2); + changePrank(signer2); nayms.executeLimitOffer(wethId, dt.entity1MintAndSaleAmt, entity1, dt.entity1MintAndSaleAmt); assertEq(nayms.getLastOfferId(), 2, "lastOfferId should INCREASE after executeLimitOffer"); vm.stopPrank(); @@ -254,11 +257,11 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { uint256 e2WethBeforeTrade = nayms.internalBalanceOf(entity2, wethId); uint256 e3WethBeforeTrade = nayms.internalBalanceOf(entity3, wethId); - vm.startPrank(signer2); + changePrank(signer2); nayms.executeLimitOffer(entity1, dt.entity1MintAndSaleAmt, wethId, dt.entity1MintAndSaleAmt); vm.stopPrank(); - vm.startPrank(signer3); + changePrank(signer3); nayms.executeLimitOffer(wethId, dt.entity1MintAndSaleAmt, entity1, dt.entity1MintAndSaleAmt); vm.stopPrank(); @@ -272,7 +275,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { // init and fund taker entity nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test"); - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, dt.entity2ExternalDepositAmt); nayms.externalDeposit(wethAddress, dt.entity2ExternalDepositAmt); @@ -289,14 +292,14 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { function testCancelOffer() public { testStartTokenSale(); - vm.startPrank(signer3); + changePrank(signer3); vm.expectRevert("only member of entity can cancel"); nayms.cancelOffer(1); vm.stopPrank(); vm.recordLogs(); - vm.startPrank(signer1); + changePrank(signer1); nayms.cancelOffer(1); Vm.Log[] memory entries = vm.getRecordedLogs(); @@ -343,10 +346,11 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { } else { uint256 e2Balance = (salePrice * (LibConstants.BP_FACTOR + c.tradingCommissionTotalBP)) / LibConstants.BP_FACTOR; - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, e2Balance); nayms.externalDeposit(wethAddress, e2Balance); - vm.stopPrank(); + + changePrank(systemAdmin); // sell x nENTITY1 for y WETH nayms.startTokenSale(entity1, saleAmount, salePrice); @@ -361,7 +365,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { assertEq(marketInfo1.buyAmountInitial, salePrice, "buy amount initial"); assertEq(marketInfo1.state, LibConstants.OFFER_STATE_ACTIVE, "state"); - vm.prank(signer2); + changePrank(signer2); nayms.executeLimitOffer(wethId, salePrice, entity1, saleAmount); assertOfferFilled(1, entity1, entity1, saleAmount, wethId, salePrice); @@ -380,9 +384,9 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, salePrice, true), "test"); // init test funds to maxint - - writeTokenBalance(signer1, naymsAddress, wethAddress, ~uint256(0)); nayms.enableEntityTokenization(entity1, "e1token", "e1token"); + changePrank(signer1); + writeTokenBalance(signer1, naymsAddress, wethAddress, ~uint256(0)); if (saleAmount == 0) { vm.expectRevert("mint amount must be > 0"); @@ -391,7 +395,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { vm.expectRevert("_internalMint: mint zero tokens"); nayms.externalDeposit(wethAddress, salePrice); } else { - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, salePrice); nayms.externalDeposit(wethAddress, salePrice); assertEq(nayms.internalBalanceOf(entity2, LibHelpers._getIdForAddress(wethAddress)), salePrice, "Entity2: invalid balance"); @@ -409,6 +413,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { vm.stopPrank(); assertEq(nayms.internalBalanceOf(entity1, LibHelpers._getIdForAddress(wethAddress)), e1Balance, "Entity1: invalid balance"); + changePrank(systemAdmin); // prob need to be system admin // sell x nENTITY1 for y WETH nayms.startTokenSale(entity1, saleAmount, salePrice); @@ -424,7 +429,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "entity test hash"); // fund taker entity - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, 1_000 ether); nayms.externalDeposit(wethAddress, 1_000 ether); @@ -455,7 +460,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { nayms.createEntity(entity4, signer4Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "entity test hash"); // fund taker entity - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, 1_000 ether); nayms.externalDeposit(wethAddress, 1_000 ether); vm.stopPrank(); @@ -491,15 +496,15 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { function testOfferValidation() public { testStartTokenSale(); - vm.startPrank(account9); + changePrank(account9); vm.expectRevert("offer must be made by an existing entity"); nayms.executeLimitOffer(wethId, dt.entity1MintAndSaleAmt, entity1, dt.entity1MintAndSaleAmt); - vm.stopPrank(); // init taker entity + changePrank(systemAdmin); nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "entity test hash"); - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, 1_000 ether); nayms.externalDeposit(wethAddress, 1_000 ether); @@ -538,10 +543,11 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { vm.stopPrank(); + changePrank(systemAdmin); nayms.enableEntityTokenization(entity2, "e2token", "e2token"); nayms.startTokenSale(entity2, dt.entity2MintAndSaleAmt, dt.entity2SalePrice); - vm.startPrank(signer3); + changePrank(signer3); vm.expectRevert("must be one participation token and one external token"); // 2 platform tokens nayms.executeLimitOffer(entity2, dt.entity1MintAndSaleAmt, entity1, dt.entity1MintAndSaleAmt); @@ -560,7 +566,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { // create (x2) counter offer nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test"); - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, dt.entity2ExternalDepositAmt * 2); nayms.externalDeposit(wethAddress, dt.entity2ExternalDepositAmt * 2); @@ -569,6 +575,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { assertOfferPartiallyFilled(2, entity2, wethId, dt.entity1MintAndSaleAmt, dt.entity1MintAndSaleAmt * 2, entity1, dt.entity1MintAndSaleAmt, dt.entity1MintAndSaleAmt * 2); + changePrank(systemAdmin); // start another nENTITY1 token sale nayms.startTokenSale(entity1, dt.entity1MintAndSaleAmt, dt.entity1SalePrice); @@ -586,7 +593,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { nayms.createEntity(entity4, signer4Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "entity test hash"); // fund taker entity - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, 1_000 ether); nayms.externalDeposit(wethAddress, 1_000 ether); vm.stopPrank(); @@ -685,7 +692,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { // Diamond cut this fixture contract into our nayms diamond in order to test against the diamond cut[0] = IDiamondCut.FacetCut({ facetAddress: address(libFeeRouterFixture), action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); - nayms.diamondCut(cut, address(0), ""); + scheduleAndUpgradeDiamond(cut); (bool success, bytes memory result) = address(nayms).call(abi.encodeWithSelector(libFeeRouterFixture.calculateTradingCommissionsFixture.selector, 10_000)); @@ -728,7 +735,9 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { nayms.addSupportedExternalToken(wethAddress); // OFFER 1: 2000 pTokens -> 2000 WETH + changePrank(account0); writeTokenBalance(account0, naymsAddress, wethAddress, e1balance); + changePrank(systemAdmin); nayms.createEntity(entity1, signer1Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test"); nayms.enableEntityTokenization(entity1, "e1token", "e1token"); @@ -737,7 +746,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { // OFFER 2 (x2) counter offer: 4000 WETH -> 4000 pTokens // we have to do this as the protocol does not allow us to create an offer to buy pTokens before they are minted! nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test"); - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, e2balance); nayms.externalDeposit(wethAddress, e2balance); nayms.executeLimitOffer(wethId, offer2sell, entity1, offer2buy); @@ -748,6 +757,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { assertOfferPartiallyFilled(2, entity2, wethId, offer1buy, offer2sell, entity1, offer1sell, offer2buy); // OFFER 3: 2000 pTokens -> 1000 WETH + changePrank(systemAdmin); nayms.startTokenSale(entity1, offer3sell, offer3buy); assertOfferFilled(1, entity1, entity1, offer1sell, wethId, offer1buy); @@ -768,14 +778,15 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { uint256 e2Balance = (salePrice * (LibConstants.BP_FACTOR + c.tradingCommissionTotalBP)) / LibConstants.BP_FACTOR; - vm.startPrank(signer2); + changePrank(signer2); writeTokenBalance(signer2, naymsAddress, wethAddress, e2Balance); nayms.externalDeposit(wethAddress, e2Balance); - vm.stopPrank(); // sell x nENTITY1 for y WETH + changePrank(systemAdmin); nayms.enableEntityTokenization(e1Id, "e1token", "e1token"); nayms.startTokenSale(e1Id, saleAmount, salePrice); + vm.stopPrank(); vm.prank(signer2); nayms.executeLimitOffer(wethId, salePrice, e1Id, saleAmount); @@ -788,6 +799,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { bytes32 policyId1 = "policy1"; uint256 policyLimit = 85 ether; + changePrank(systemAdmin); (Stakeholders memory stakeholders, SimplePolicy memory policy) = initPolicyWithLimit(testPolicyDataHash, policyLimit); nayms.createSimplePolicy(policyId1, e1Id, stakeholders, policy, testPolicyDataHash); @@ -795,7 +807,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts { assertEq(lockedBalance, policyLimit, "locked balance should increase"); vm.expectRevert("insufficient balance"); - vm.prank(signer1); + changePrank(signer1); nayms.executeLimitOffer(e1Id, salePrice, wethId, saleAmount); } } diff --git a/test/T05TokenWrapper.t.sol b/test/T05TokenWrapper.t.sol index 002cd688..0b5e7597 100644 --- a/test/T05TokenWrapper.t.sol +++ b/test/T05TokenWrapper.t.sol @@ -64,6 +64,7 @@ contract T05TokenWrapper is D03ProtocolDefaults { assertEq(wrapper.totalSupply(), nayms.internalTokenSupply(entityId1), "token supply should match"); assertEq(wrapper.totalSupply(), tokenAmount, "token supply should match sale amount"); + changePrank(account0); nayms.cancelOffer(1); // unlock tokens from market, to enable transfer nayms.internalTransferFromEntity(account0Id, entityId1, tokenAmount); assertEq(wrapper.balanceOf(account0), nayms.internalBalanceOf(account0Id, entityId1), "wrapper balance should match diamond"); @@ -79,7 +80,7 @@ contract T05TokenWrapper is D03ProtocolDefaults { assertEq(wrapper.balanceOf(signer1), tokenAmount, "signer1 balance should increase"); assertEq(wrapper.allowance(signer1, account0), 0, "allowance should be 0"); - vm.startPrank(signer1); + changePrank(signer1); wrapper.approve(account0, tokenAmount); vm.stopPrank(); diff --git a/test/defaults/D00GlobalDefaults.sol b/test/defaults/D00GlobalDefaults.sol index c994092b..b8414bd7 100644 --- a/test/defaults/D00GlobalDefaults.sol +++ b/test/defaults/D00GlobalDefaults.sol @@ -9,17 +9,20 @@ import "test/utils/DSTestPlusF.sol"; import { AppStorage } from "src/diamonds/nayms/AppStorage.sol"; import { MockAccounts } from "test/utils/users/MockAccounts.sol"; +// deployer, owner, system admin +// For local tests, account0 will be the owner and the deployer. This will be the test contract address. +// systemAdmin will be another account. owner and system admins must be mutually exclusive. + contract D00GlobalDefaults is DSTestPlusF { - address public immutable account0 = address(this); + address public account0 = address(this); + uint256 public MAINNET_FORK_BLOCK_NUMBER = 15615850; uint256 public GOERLI_FORK_BLOCK_NUMBER = 7661570; function setUp() public virtual { console2.log("\n -- D00 Global Defaults\n"); - console2.log("Test contract address, aka account0", address(this)); + console2.log("Test contract address, aka account0, deployer, owner", address(this)); console2.log("msg.sender during setup", msg.sender); - - vm.label(account0, "Account 0 (Test Contract address)"); } } diff --git a/test/defaults/D01Deployment.sol b/test/defaults/D01Deployment.sol index eb1e35aa..5488bfd5 100644 --- a/test/defaults/D01Deployment.sol +++ b/test/defaults/D01Deployment.sol @@ -4,8 +4,8 @@ pragma solidity 0.8.17; import "./D00GlobalDefaults.sol"; import { InitDiamond } from "src/diamonds/nayms/InitDiamond.sol"; +import { INayms, IDiamondCut } from "src/diamonds/nayms/INayms.sol"; import { Nayms } from "src/diamonds/nayms/Nayms.sol"; -import { INayms } from "src/diamonds/nayms/INayms.sol"; import { LibAdmin } from "src/diamonds/nayms/libs/LibAdmin.sol"; import { LibConstants } from "src/diamonds/nayms/libs/LibConstants.sol"; import { LibHelpers } from "src/diamonds/nayms/libs/LibHelpers.sol"; @@ -29,6 +29,11 @@ contract D01Deployment is //// test constant variables //// bytes32 public immutable salt = keccak256(bytes("A salt!")); + address public systemAdmin; + bytes32 public systemAdminId; + address public owner; + address public deployer; + function setUp() public virtual override { super.setUp(); @@ -38,8 +43,14 @@ contract D01Deployment is // deploy all facets address[] memory naymsFacetAddresses = LibGeneratedNaymsFacetHelpers.deployNaymsFacets(); - // deterministically deploy Nayms diamond - nayms = INayms(address(new Nayms(address(this)))); + owner = account0; + deployer = account0; + vm.label(account0, "Account 0 (Test Contract address, deployer, owner)"); + + systemAdmin = makeAddr("System Admin 0"); + systemAdminId = LibHelpers._getIdForAddress(systemAdmin); + // vm.label(systemAdmin, "System Admin"); + nayms = INayms(address(new Nayms(owner, systemAdmin))); naymsAddress = address(nayms); // initialize the diamond as well as cut in all facets @@ -48,4 +59,38 @@ contract D01Deployment is // vm.prank(msg.sender); nayms.diamondCut(cut, address(initDiamond), abi.encodeCall(initDiamond.initialize, ())); } + + function scheduleAndUpgradeDiamond(IDiamondCut.FacetCut[] memory _cut) internal { + // 1. schedule upgrade + // 2. upgrade + bytes32 upgradeHash = keccak256(abi.encode(_cut, address(0), "")); + if (upgradeHash == 0xc597f3eb22d11c46f626cd856bd65e9127b04623d83e442686776a2e3b670bbf) { + console2.log("There are no facets to upgrade. This hash is the keccak256 hash of an empty IDiamondCut.FacetCut[]"); + } else { + changePrank(systemAdmin); + nayms.createUpgrade(upgradeHash); + changePrank(owner); + nayms.diamondCut(_cut, address(0), new bytes(0)); + changePrank(systemAdmin); + } + } + + function scheduleAndUpgradeDiamond( + IDiamondCut.FacetCut[] memory _cut, + address _init, + bytes memory _calldata + ) internal { + // 1. schedule upgrade + // 2. upgrade + bytes32 upgradeHash = keccak256(abi.encode(_cut, _init, _calldata)); + if (upgradeHash == 0xc597f3eb22d11c46f626cd856bd65e9127b04623d83e442686776a2e3b670bbf) { + console2.log("There are no facets to upgrade. This hash is the keccak256 hash of an empty IDiamondCut.FacetCut[]"); + } else { + changePrank(systemAdmin); + nayms.createUpgrade(upgradeHash); + changePrank(owner); + nayms.diamondCut(_cut, _init, _calldata); + changePrank(systemAdmin); + } + } } diff --git a/test/defaults/D03ProtocolDefaults.sol b/test/defaults/D03ProtocolDefaults.sol index 623d39c9..d23dbbdc 100644 --- a/test/defaults/D03ProtocolDefaults.sol +++ b/test/defaults/D03ProtocolDefaults.sol @@ -12,7 +12,7 @@ import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /// Protocol / project level defaults /// Setup internal token IDs, entities, contract D03ProtocolDefaults is D02TestSetup { - bytes32 public immutable account0Id = LibHelpers._getIdForAddress(address(this)); + bytes32 public immutable account0Id = LibHelpers._getIdForAddress(account0); bytes32 public naymsTokenId; bytes32 public immutable systemContext = LibAdmin._getSystemId(); @@ -50,6 +50,7 @@ contract D03ProtocolDefaults is D02TestSetup { vm.label(signer3, "Account 3 (Capital Provider Rep)"); vm.label(signer4, "Account 4 (Insured Party Rep)"); + changePrank(systemAdmin); nayms.addSupportedExternalToken(wethAddress); Entity memory entity = Entity({ @@ -70,7 +71,6 @@ contract D03ProtocolDefaults is D02TestSetup { } function createTestEntity(bytes32 adminId) internal returns (bytes32 entityId) { - // create entity with signer2 as child entityId = "0xe1"; Entity memory entity1 = initEntity(wethId, 5000, 10000, false); nayms.createEntity(entityId, adminId, entity1, bytes32(0));