diff --git a/.gitignore b/.gitignore
index 05f5c0ef..6030edd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,20 +41,8 @@ facetsdeployed.txt
.DS_Store
bin/
-.idea/.gitignore
-.idea/contracts-next.iml
-.idea/misc.xml
-.idea/modules.xml
-.idea/runConfigurations.xml
-.idea/vcs.xml
-.metals
-.supermaven
broadcast/**/31337/*
-
-# subgraph artifact
-NaymsDiamond.json
-
broadcast/**/dry-run
# coverage report
@@ -64,5 +52,3 @@ cov-html
# gemforge.deployments.json
.gemforge/*.json
-# Generated files
-script/deployment/S03UpgradeDiamond.s.sol
diff --git a/.gitmodules b/.gitmodules
index 9870147f..34428133 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,15 +7,6 @@
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
-[submodule "lib/solidity-lib"]
- path = lib/solidity-lib
- url = https://github.com/uniswap/solidity-lib
-[submodule "lib/v3-periphery-foundry"]
- path = lib/v3-periphery-foundry
- url = https://github.com/gakonst/v3-periphery-foundry
-[submodule "lib/solidity-stringutils"]
- path = lib/solidity-stringutils
- url = https://github.com/arachnid/solidity-stringutils
[submodule "lib/oz"]
path = lib/oz
url = https://github.com/openzeppelin/openzeppelin-contracts
diff --git a/.solhint.json b/.solhint.json
index 186b2ec1..a3b53897 100644
--- a/.solhint.json
+++ b/.solhint.json
@@ -9,6 +9,7 @@
"const-name-snakecase": "off",
"reason-string": "off",
"var-name-mixedcase": "off",
- "func-name-mixedcase": "off"
+ "func-name-mixedcase": "off",
+ "immutable-vars-naming": "off"
}
}
diff --git a/Makefile b/Makefile
index 2bf249e5..d0452963 100644
--- a/Makefile
+++ b/Makefile
@@ -63,7 +63,7 @@ gasforksnap: ## gas snapshot mainnet fork
forge snapshot --snap .gas-snapshot \
-f ${ETH_MAINNET_RPC_URL} \
--fork-block-number 15078000
-
+
gasforkcheck: ## gas check mainnet fork
forge snapshot --check \
-f ${ETH_MAINNET_RPC_URL} \
@@ -82,9 +82,6 @@ cov: ## coverage report -vvv
coverage: ## coverage report (lcov), filtered for CI
forge coverage --no-match-test testFork -vvv --report lcov --via-ir && node ./cli-tools/filter-lcov.js
-lcov: ## coverage report (lcov)
- forge coverage --report lcov --via-ir
-
gencov: ## generate html coverage report
forge coverage --report lcov && genhtml -o cov-html --branch-coverage lcov.info
@@ -139,9 +136,6 @@ anvil-docker: ## run anvil in a container
ghcr.io/nayms/contracts-builder:latest \
-c "cd nayms && make anvil"
-anvil-dbg: ## run anvil in debug mode with shared wallet
- RUST_LOG=backend,api,node,rpc=warn anvil --host 0.0.0.0 --chain-id 31337 -m "${shell cat ./nayms_mnemonic.txt}" --state anvil.json
-
fork-mainnet: ## fork mainnet locally with anvil
anvil -f ${ETH_MAINNET_RPC_URL} --accounts 20 -m "${shell cat ./nayms_mnemonic.txt}"
@@ -208,72 +202,9 @@ create-entity: ## create an entity on the Nayms platform (using some default val
-vv \
--broadcast
-update-entity: ## update
- forge script UpdateEntity \
- -f ${ETH_GOERLI_RPC_URL} \
- --chain-id 5 \
- --sender ${ownerAddress} \
- --mnemonic-paths ./nayms_mnemonic.txt \
- --mnemonic-indexes 19 \
- -vvvv \
- --broadcast
-
-update-commissions: ## update trading and premium commissions
- forge script UpdateCommissions \
- -s "tradingAndPremium(address)" ${naymsDiamondAddress} \
- -f ${ETH_GOERLI_RPC_URL} \
- --chain-id 5 \
- --sender ${ownerAddress} \
- --mnemonic-paths ./nayms_mnemonic.txt \
- --mnemonic-indexes 19 \
- -vv \
- --broadcast
-
-docs: ## generate docs from natspec comments
- yarn docgen
-
slither: ## run slither static analysis
slither src/generated --config-file=slither.config.json --fail-none
-verify-dry-run: ## dry run verify script, prints out commands to be executed
- node cli-tools/verify.js --dry-run
-
-verify: ## verify contracts on chain (goerli)
- node cli-tools/verify.js
-
-coderecon: ## code recon
- @forge script CodeRecon \
- -s "run(string[] memory)" ${contractNames} \
- -f ${ETH_MAINNET_RPC_URL} \
- --chain-id 1 \
- --etherscan-api-key ${ETHERSCAN_API_KEY} \
- -vv \
- ; node cli-tools/parse-json.js
-
-compb: ## Compare bytecode
- @forge script CheckBytecode \
- -s "run(uint8)" ${checkBytecodeAction} \
- -f ${ETH_MAINNET_RPC_URL} \
- --chain-id 1 \
- --etherscan-api-key ${ETHERSCAN_API_KEY} \
- --sender ${senderAddress} \
- --mnemonic-paths ./nayms_mnemonic.txt \
- --mnemonic-indexes 19 \
- -v \
- --ffi
-
-checkf: ## Check if facet exists in a diamond
- @forge script DiamondChecker \
- -s "run(address, bytes4)" ${chkFacetAddress} ${selectorChk} \
- -f ${ETH_SEPOLIA_RPC_URL} \
- --chain-id 11155111 \
- --etherscan-api-key ${ETHERSCAN_API_KEY} \
- --sender ${ownerAddress} \
- --mnemonic-paths ./nayms_mnemonic.txt \
- --mnemonic-indexes 19 \
- -vv \
- --ffi
-
bn-mainnet: ## get block number for mainnet and replace FORK_BLOCK_1 in .env
@result=$$(cast bn -r mainnet) && \
sed -i '' "s/^export FORK_BLOCK_1=.*/export FORK_BLOCK_1=$$result/" .env
diff --git a/README.md b/README.md
index d2ed237e..1ef9dc39 100644
--- a/README.md
+++ b/README.md
@@ -108,10 +108,7 @@ Following commands are provided for working with `anvil`, to make it more conven
| Command | Description |
| ----------- | ----------- |
| `make anvil` | Run the local node seeding it with Nayms' shared wallet |
-| `make anvil-debug` | Run Anvil in debug mode to get verbose log output |
-| `make anvil-deploy` | Do a full deployment of Nayms' contracts to local node |
-| `make anvil-upgrade` | Upgrade deployment of Nayms' contracts on local node |
-| `make anvil-gtoken` | Deploy `GToken` to local node |
+| `make anvil-docker` | Rund the local node inside a container, also seeding it with Nayms' shared wallet |
| `make anvil-add-supported-external-token` | Add `GToken` as supported external token |
| `make fork-sepolia`| Fork `Sepolia` test net locally |
| `make fork-base-sepolia`| Fork `Base Sepolia` test net locally |
diff --git a/cli-tools/contract.hbs b/cli-tools/contract.hbs
deleted file mode 100644
index 35c494fb..00000000
--- a/cli-tools/contract.hbs
+++ /dev/null
@@ -1,83 +0,0 @@
-
-{{{title}}}
-{{{natspec.userdoc}}}
-
-{{#if ownVariables}}
-## Globals
-
-> Note this contains internal vars as well due to a bug in the docgen procedure
-
-| Var | Type |
-| --- | --- |
-{{#ownVariables}}
-{{#unless (eq visibility "internal")}}
-| {{name}} | {{ type }} |
-{{/unless}}
-{{/ownVariables}}
-{{/if}}
-
-
-{{#if ownFunctions}}
-## Functions
-
-{{#ownFunctions}}
-### {{name}}
-{{#if natspec.userdoc}}{{natspec.userdoc}}{{else}}No description{{/if}}
-{{#if natspec.devdoc}}{{natspec.devdoc}}{{/if}}
-
-```solidity
- function {{name}}(
- {{#natspec.params}}
- {{#lookup ../args.types @index}}{{/lookup}} {{param}}{{#if @last}}{{else}},{{/if}}
- {{/natspec.params}}
- ) {{visibility}}{{#astNode.modifiers}} {{modifierName.name}}{{/astNode.modifiers}}{{#if outputs}} returns ({{outputs}}){{/if}}
-```
-
-{{#if natspec.params}}
-#### Arguments:
-| Argument | Type | Description |
-| --- | --- | --- |
-{{#natspec.params}}
-|`{{param}}` | {{#lookup ../args.types @index}}{{/lookup}} | {{description}}
-{{~/natspec.params}}
-|
-
-{{/if}}
-
-{{#if natspec.returns}}
-#### Returns:
-| Type | Description |
-| --- | --- |
-{{#natspec.returns}}
-|`{{param}}` | {{description}}
-{{~/natspec.returns}}
-|
-
-{{/if}}
-
-{{/ownFunctions}}
-{{/if}}
-
-
-{{#if ownEvents}}
-## Events
-
-{{#ownEvents}}
-### {{name}}
-{{#if natspec.userdoc}}{{natspec.userdoc}}{{else}}No description{{/if}}
-{{#if natspec.devdoc}}> {{natspec.devdoc}}{{/if}}
-
-
-{{#if natspec.params}}
-#### Params:
-| Param | Type | Indexed | Description |
-| --- | --- | :---: | --- |
-{{#natspec.params}}
-|`{{param}}` | {{lookup ../args.types @index}} | {{#with (lookup ../astNode.parameters.parameters @index)}}{{#if indexed}}:white_check_mark:{{/if}}{{/with}} | {{description}} {{~-~}}
-{{/natspec.params}}
-|
-
-{{/if}}
-
-{{/ownEvents}}
-{{/if}}
\ No newline at end of file
diff --git a/cli-tools/contract_original.hbs b/cli-tools/contract_original.hbs
deleted file mode 100644
index 94b10875..00000000
--- a/cli-tools/contract_original.hbs
+++ /dev/null
@@ -1,77 +0,0 @@
-
-{{{title}}}
-{{{natspec.userdoc}}}
-
-{{#if ownVariables}}
-## Globals
-
-> Note this contains internal vars as well due to a bug in the docgen procedure
-
-| Var | Type |
-| --- | --- |
-{{#ownVariables}}
-{{#unless (eq visibility "internal")}}
-| {{name}} | {{ type }} |
-{{/unless}}
-{{/ownVariables}}
-{{/if}}
-
-
-{{#if ownFunctions}}
-## Functions
-
-{{#ownFunctions}}
-### {{name}}
-{{#if natspec.userdoc}}{{natspec.userdoc}}{{else}}No description{{/if}}
-{{#if natspec.devdoc}}{{natspec.devdoc}}{{/if}}
-
-```solidity
- function {{name}}(
- {{#natspec.params}}
- {{#lookup ../args.types @index}}{{/lookup}} {{param}}{{#if @last}}{{else}},{{/if}}
- {{/natspec.params}}
- ) {{visibility}}{{#astNode.modifiers}} {{modifierName.name}}{{/astNode.modifiers}}{{#if outputs}} returns ({{outputs}}){{/if}}
-```
-
-{{#if natspec.params}}
-#### Arguments:
-| Argument | Type | Description |
-| --- | --- | --- |
-{{#natspec.params}}
-|`{{param}}` | {{#lookup ../args.types @index}}{{/lookup}} | {{description}}
-{{~/natspec.params}}
-{{/if}}
-
-{{#if natspec.returns}}
-#### Returns:
-| Type | Description |
-| --- | --- |
-{{#natspec.returns}}
-|`{{param}}` | {{description}}
-{{~/natspec.returns}}
-{{/if}}
-
-{{/ownFunctions}}
-{{/if}}
-
-
-{{#if ownEvents}}
-## Events
-
-{{#ownEvents}}
-### {{name}}
-{{#if natspec.userdoc}}{{natspec.userdoc}}{{else}}No description{{/if}}
-{{#if natspec.devdoc}}> {{natspec.devdoc}}{{/if}}
-
-
-{{#if natspec.params}}
-#### Params:
-| Param | Type | Indexed | Description |
-| --- | --- | :---: | --- |
-{{#natspec.params}}
-|`{{param}}` | {{lookup ../args.types @index}} | {{#with (lookup ../astNode.parameters.parameters @index)}}{{#if indexed}}:white_check_mark:{{/if}}{{/with}} | {{description}} {{~-~}}
-{{/natspec.params}}
-{{/if}}
-
-{{/ownEvents}}
-{{/if}}
\ No newline at end of file
diff --git a/cli-tools/docgen.js b/cli-tools/docgen.js
deleted file mode 100644
index ed960a18..00000000
--- a/cli-tools/docgen.js
+++ /dev/null
@@ -1,65 +0,0 @@
-const NODE_DIR = "./node_modules";
-const INPUT_DIR = "./src/diamonds/nayms/interfaces";
-const CONFIG_DIR = "./cli-tools";
-const OUTPUT_DIR = "./docs/facets";
-const README_FILE = "./docs/index.md";
-const SUMMARY_FILE = "./docs/summary.md";
-const EXCLUDES = "./src/utils";
-
-const fs = require("fs");
-const path = require("path");
-const spawnSync = require("child_process").spawnSync;
-
-const excludeList = EXCLUDES.split(",");
-const relativePath = path.relative(path.dirname(SUMMARY_FILE), OUTPUT_DIR);
-
-function lines(pathName) {
- return fs.readFileSync(pathName, {encoding: "utf8"}).split("\r").join("").split("\n");
-}
-
-function scan(pathName, indentation) {
- if (!excludeList.includes(pathName)) {
- if (fs.lstatSync(pathName).isDirectory()) {
- fs.appendFileSync(SUMMARY_FILE, indentation + "* " + path.basename(pathName) + "\n");
- for (const fileName of fs.readdirSync(pathName))
- scan(pathName + "/" + fileName, indentation + " ");
- }
- else if (pathName.endsWith("Facet.sol")) {
- const text = path.basename(pathName).slice(0, -4);
- const link = pathName.slice(INPUT_DIR.length, -4);
- fs.appendFileSync(SUMMARY_FILE, indentation + "* [" + text + "](" + relativePath + link + ".md)\n");
- }
- }
-}
-
-function fix(pathName) {
- if (fs.lstatSync(pathName).isDirectory()) {
- for (const fileName of fs.readdirSync(pathName))
- fix(pathName + "/" + fileName);
- }
- else if (pathName.endsWith(".md")) {
- fs.writeFileSync(pathName, lines(pathName).filter(line => line.trim().length > 0).join("\n") + "\n");
- }
-}
-
-fs.writeFileSync (SUMMARY_FILE, "\n# Summary\n\n");
-
-scan(INPUT_DIR, "");
-
-const args = [
- NODE_DIR + "/solidity-docgen/dist/cli.js",
- "--input=" + INPUT_DIR,
- "--output=" + OUTPUT_DIR,
- "--templates=" + CONFIG_DIR,
- "--exclude=" + EXCLUDES,
- "--solc-module=" + NODE_DIR + "/solc",
- '--solc-settings=' +
- JSON.stringify({ optimizer: { enabled: true, runs: 200 },
- })
-];
-
-const result = spawnSync("node", args, {stdio: ["inherit", "inherit", "pipe"]});
-if (result.stderr.length > 0)
- throw new Error(result.stderr);
-
-fix(OUTPUT_DIR);
diff --git a/gemforge.config.cjs b/gemforge.config.cjs
index f96c12d7..34cd7acf 100644
--- a/gemforge.config.cjs
+++ b/gemforge.config.cjs
@@ -1,12 +1,14 @@
require("dotenv").config();
const fs = require("fs");
-const ethers = require("ethers");
+const { ethers, utils } = require("ethers");
const MNEMONIC = fs.existsSync("./nayms_mnemonic.txt") ? fs.readFileSync("./nayms_mnemonic.txt").toString().trim() : "test test test test test test test test test test test junk";
const sysAdminAddress = ethers.Wallet.fromMnemonic(MNEMONIC)?.address;
+const localSalt = utils.keccak256(utils.toUtf8Bytes("salty3"));
+
module.exports = {
// Configuration file version
version: 2,
@@ -79,7 +81,7 @@ module.exports = {
preBuild: "",
postBuild: "",
preDeploy: "",
- postDeploy: "./script/gemforge/verify.js",
+ postDeploy: "",
},
// Wallets to use for deployment
wallets: {
@@ -108,56 +110,64 @@ module.exports = {
},
networks: {
local: { rpcUrl: "http://localhost:8545" },
- sepolia: { rpcUrl: process.env.ETH_SEPOLIA_RPC_URL },
- mainnet: { rpcUrl: process.env.ETH_MAINNET_RPC_URL },
+ sepolia: {
+ rpcUrl: process.env.ETH_SEPOLIA_RPC_URL,
+ contractVerification: {
+ foundry: {
+ apiUrl: "https://api-sepolia.etherscan.io/api",
+ apiKey: () => process.env.ETHERSCAN_API_KEY,
+ },
+ },
+ },
+ mainnet: {
+ rpcUrl: process.env.ETH_MAINNET_RPC_URL,
+ contractVerification: {
+ foundry: {
+ apiUrl: "https://api.etherscan.io/api",
+ apiKey: () => process.env.ETHERSCAN_API_KEY,
+ },
+ },
+ },
baseSepolia: {
rpcUrl: process.env.BASE_SEPOLIA_RPC_URL,
- verifiers: [
- {
- verifierName: "etherscan",
- verifierUrl: "https://api-sepolia.basescan.org/api",
- verifierApiKey: process.env.BASESCAN_API_KEY,
- },
- {
- verifierName: "blockscout", // needed for louper
- verifierUrl: "https://base-sepolia.blockscout.com/api",
- verifierApiKey: process.env.BLOCKSCOUT_API_KEY,
+ contractVerification: {
+ foundry: {
+ apiUrl: "https://api-sepolia.basescan.org/api",
+ apiKey: () => process.env.BASESCAN_API_KEY,
},
- ],
+ },
},
base: {
rpcUrl: process.env.BASE_MAINNET_RPC_URL,
- verifiers: [
- {
- verifierName: "etherscan",
- verifierUrl: "https://api.basescan.org/api",
- verifierApiKey: process.env.BASESCAN_API_KEY,
+ contractVerification: {
+ foundry: {
+ apiUrl: "https://api.basescan.org/api",
+ apiKey: () => process.env.BASESCAN_API_KEY,
},
- ],
+ },
},
aurora: {
rpcUrl: process.env.AURORA_MAINNET_RPC_URL,
- verifiers: [
- {
- verifierName: "aurora",
- verifierUrl: "https://explorer.mainnet.aurora.dev/api",
- verifierApiKey: process.env.BLOCKSCOUT_API_KEY,
+ contractVerification: {
+ foundry: {
+ apiUrl: "https://explorer.mainnet.aurora.dev/api",
+ apiKey: () => process.env.BLOCKSCOUT_API_KEY,
},
- ],
+ },
},
auroraTestnet: {
rpcUrl: process.env.AURORA_TESTNET_RPC_URL,
- verifiers: [
- {
- verifierName: "aurora",
- verifierUrl: "https://explorer.testnet.aurora.dev/api",
+ contractVerification: {
+ foundry: {
+ apiUrl: "https://explorer.testnet.aurora.dev/api",
+ apiKey: () => process.env.BLOCKSCOUT_API_KEY,
},
- ],
+ },
},
},
targets: {
// `governance` attribute is only releveant for testnets, it's a wallet to use to auto approve the upgrade ID within the script
- local: { network: "local", wallet: "devOwnerWallet", governance: "devSysAdminWallet", initArgs: [sysAdminAddress] },
+ local: { network: "local", wallet: "devOwnerWallet", governance: "devSysAdminWallet", initArgs: [sysAdminAddress], create3Salt: localSalt },
sepolia: { network: "sepolia", wallet: "devOwnerWallet", governance: "devSysAdminWallet", initArgs: [sysAdminAddress] },
sepoliaFork: { network: "local", wallet: "devOwnerWallet", governance: "devSysAdminWallet", initArgs: [sysAdminAddress] },
mainnet: { network: "mainnet", wallet: "wallet3", initArgs: [sysAdminAddress] },
diff --git a/gemforge.deployments.json b/gemforge.deployments.json
index c44ff442..cc0727ed 100644
--- a/gemforge.deployments.json
+++ b/gemforge.deployments.json
@@ -6,9 +6,9 @@
"name": "DiamondProxy",
"fullyQualifiedName": "DiamondProxy.sol:DiamondProxy",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x6d1ea0b274d7af0ca022b64f517f4f3be762995d87552d77a6f4ee2ff6978cbe",
+ "txHash": "0x1a2fa746d593988476cf71e5124a537683cbfc3bd171b5eaaf64c51e2c0cefec",
"onChain": {
- "address": "0x1e560E6adDF76b9335540565a96F4a93f371a56c",
+ "address": "0xE9A592C9d6f20eBe2eB302FAC5cd31EAF984Bb48",
"constructorArgs": [
"0x931c3aC09202650148Edb2316e97815f904CF4fa"
]
@@ -18,9 +18,9 @@
"name": "ACLFacet",
"fullyQualifiedName": "ACLFacet.sol:ACLFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xa46590e8de7d4ec32c712272b25feab6c651d8d01b10054d7c0f30c0e365adb5",
+ "txHash": "0x33f4ba195159855ccf9d35ed8ccd6c353e224a2716ce3da719e6abca9207427d",
"onChain": {
- "address": "0xeb91C729D4bD06F41C7624E6ef0a40a828479af2",
+ "address": "0xd6c79E894570d90739c44c2923F84C276567ABf8",
"constructorArgs": []
}
},
@@ -28,9 +28,9 @@
"name": "AdminFacet",
"fullyQualifiedName": "AdminFacet.sol:AdminFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x40f1d62e861f6017761819b20d72e3dd703001bcc9a77c526da50ed81417bd06",
+ "txHash": "0xb6534dbc917142f577e48909174708bf6c9cd65d70d721fc27318e689ead5c7a",
"onChain": {
- "address": "0xd6c79E894570d90739c44c2923F84C276567ABf8",
+ "address": "0x4F10acBA59A206a66713380De02F9c09880A822F",
"constructorArgs": []
}
},
@@ -38,9 +38,9 @@
"name": "EntityFacet",
"fullyQualifiedName": "EntityFacet.sol:EntityFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xb61c01169c230a60c43b87024d125b5112646728ff4777cf35f0b60a72d9fba7",
+ "txHash": "0xca3cae17c63189d49484cac876b94aa253e6635e31155fd5e881ac47d8df277c",
"onChain": {
- "address": "0x4F10acBA59A206a66713380De02F9c09880A822F",
+ "address": "0x175a1077Ccdb3bF380db95889610BF6877Fb104C",
"constructorArgs": []
}
},
@@ -48,9 +48,9 @@
"name": "GovernanceFacet",
"fullyQualifiedName": "GovernanceFacet.sol:GovernanceFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xff78b3f790000769bf5348c3c8566076c5721ee3e71a0ddb3d1290acab08d4f1",
+ "txHash": "0xf8fb9c2a40b7f6fa48100c29f423e056b5ca8bf80f8002529a222a58c20d1882",
"onChain": {
- "address": "0x175a1077Ccdb3bF380db95889610BF6877Fb104C",
+ "address": "0x76Ab1953794E1a7F522ADaf20112501dFc671b9f",
"constructorArgs": []
}
},
@@ -58,9 +58,9 @@
"name": "MarketFacet",
"fullyQualifiedName": "MarketFacet.sol:MarketFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xa987f01c5a7126ab2fcd8711ace03a4ef07f2e6c329849f8988d74a9ca1ca7bd",
+ "txHash": "0x195abb03cd273c35f5d337ad311c16d6cbd9b244cb9d4f39222d339cc78ecf4e",
"onChain": {
- "address": "0x76Ab1953794E1a7F522ADaf20112501dFc671b9f",
+ "address": "0xC0009342F733B8338C933505B3CE3F30285bb439",
"constructorArgs": []
}
},
@@ -68,9 +68,9 @@
"name": "NaymsOwnershipFacet",
"fullyQualifiedName": "NaymsOwnershipFacet.sol:NaymsOwnershipFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x497ced92dcf3a87bb44cca951f81c5d2cb8ba41d536697e20c72f2fdb1390db7",
+ "txHash": "0xff19b91167d3f5dc2a661fef32ef97ac71033a4f8737fd1e58ba4ee55ab63782",
"onChain": {
- "address": "0xC0009342F733B8338C933505B3CE3F30285bb439",
+ "address": "0xfADB5dB5d08BBf950256f79C3665CE8d71d8710f",
"constructorArgs": []
}
},
@@ -78,9 +78,9 @@
"name": "PhasedDiamondCutFacet",
"fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xb6f4c23ee3e42886b9332cc043d8e070983b8da0bfe8b06785b338189d6114b6",
+ "txHash": "0x9b71dc92a492001c31a41b861696b1bb26a60cd6e17b1dd1001309827d722b3c",
"onChain": {
- "address": "0xfADB5dB5d08BBf950256f79C3665CE8d71d8710f",
+ "address": "0x60EA14Bad701260fAE76F97639BC568F4e750b29",
"constructorArgs": []
}
},
@@ -88,9 +88,9 @@
"name": "SimplePolicyFacet",
"fullyQualifiedName": "SimplePolicyFacet.sol:SimplePolicyFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x4fe595cf6ad17e30da36fd71f51bb3767cf03e36f6b73e9eefffd65c161b6c02",
+ "txHash": "0x47c857cdeb477a4594d3e4b8a64cd21233dd4ba87a80dbcc393e45fb4cf5ac49",
"onChain": {
- "address": "0x60EA14Bad701260fAE76F97639BC568F4e750b29",
+ "address": "0x2F64b09a41b400a58D4485f08f6BCF14D944Ad6f",
"constructorArgs": []
}
},
@@ -98,9 +98,9 @@
"name": "StakingFacet",
"fullyQualifiedName": "StakingFacet.sol:StakingFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xf9e659777e5f136ada71305f75d4c746bcb33878d7d9d35e908ac039b171edff",
+ "txHash": "0xa077a8182a8c815af612621376515f8c496acaef01724aca1276d6bea8d57c05",
"onChain": {
- "address": "0x2F64b09a41b400a58D4485f08f6BCF14D944Ad6f",
+ "address": "0xE8737e94DcaA61B3354644D3a7177d91Abb7fBC0",
"constructorArgs": []
}
},
@@ -108,9 +108,9 @@
"name": "SystemFacet",
"fullyQualifiedName": "SystemFacet.sol:SystemFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x212d2448f9ef55c8163d46f678f2f83601c3be8b8bfe01d55285b74cbfdd11b4",
+ "txHash": "0xca22c45514b99d29222da5d2132cdc3028a1acd2e416acb1c3e50bca6d6326cb",
"onChain": {
- "address": "0xE8737e94DcaA61B3354644D3a7177d91Abb7fBC0",
+ "address": "0xC09f543dD405347105146BfD5b799233c69A7C70",
"constructorArgs": []
}
},
@@ -118,9 +118,9 @@
"name": "TokenizedVaultFacet",
"fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xd645b5b1325b244e2b07f287e422bd1d0b544b16ac93d2a0b50bd1b1bca2e8fb",
+ "txHash": "0xbfec3170742a3ea986cab5506bf9d7432fbbcc9b0784b9d23a727539164f6ee6",
"onChain": {
- "address": "0xC09f543dD405347105146BfD5b799233c69A7C70",
+ "address": "0x0c6815cEB188B0d877B08CF4B1F850Ed0F0929F0",
"constructorArgs": []
}
},
@@ -128,9 +128,9 @@
"name": "TokenizedVaultIOFacet",
"fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x93bdf006a5cd2300826c18e284de1e8d4122740abb8b9b93bb42a8c9bff09f6a",
+ "txHash": "0x0dc76eccacd37100e0b6e9a25239e96821158ff3871bbeb3af36ea8db169034d",
"onChain": {
- "address": "0x0c6815cEB188B0d877B08CF4B1F850Ed0F0929F0",
+ "address": "0xfc12A71BF96d541F439C79E3F16654e1c9B97935",
"constructorArgs": []
}
},
@@ -138,9 +138,9 @@
"name": "UserFacet",
"fullyQualifiedName": "UserFacet.sol:UserFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x61b62d4a18d8cfb5b6c1bf1d2dffaac85ce721305d984e01539b0dc3fde3c6b5",
+ "txHash": "0x9102d52e3272b61d0e75ab75dfd753ec1819191bd9a5779da711b43dfe2c73c7",
"onChain": {
- "address": "0xfc12A71BF96d541F439C79E3F16654e1c9B97935",
+ "address": "0x002d2970F2AacFD2344d2C1cc35b3985A374A73C",
"constructorArgs": []
}
},
@@ -148,9 +148,9 @@
"name": "InitDiamond",
"fullyQualifiedName": "InitDiamond.sol:InitDiamond",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xdcc6187f91f35071ba0252722041c056cec6b565221e4176692c9d60f1848d4d",
+ "txHash": "0xa2647f54fe69e069fcda71cdf4b9b10cb1c9deebb91de7bee1a2a9e0d9b97c93",
"onChain": {
- "address": "0x002d2970F2AacFD2344d2C1cc35b3985A374A73C",
+ "address": "0x23c9080C0A5236C6E6297fB1f4184C9f200a1A80",
"constructorArgs": []
}
}
@@ -305,12 +305,12 @@
"chainId": 11155111,
"contracts": [
{
- "name": "PhasedDiamondCutFacet",
- "fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
+ "name": "TokenizedVaultFacet",
+ "fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xc48d3887b66dea8c4da120e22eb8c84f66ce73ae6558984d0a57eab1bfdbf66f",
+ "txHash": "0xd08283849d4f9c6a14cdfc047e2c7f590035d52beed286157946b042380ef973",
"onChain": {
- "address": "0x601211989B6B9EF4e792cb8c6e9a246703f5fc5B",
+ "address": "0xF3fF0876A4f4BD7f854085AF5638A66282a059B9",
"constructorArgs": []
}
},
@@ -318,9 +318,9 @@
"name": "ACLFacet",
"fullyQualifiedName": "ACLFacet.sol:ACLFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x8da2331eca87bc64bdfe512fec2269135a10f40769d6aee61564708953c0654b",
+ "txHash": "0x1c1db055eb1e6277dc155c3f336f87220b881404fd40cbd0a3b3ec903d4fb184",
"onChain": {
- "address": "0x3C1D30e508b0d919B1Af5bBb3c3813C4B261C2f3",
+ "address": "0x2B1973557a7b4409B2668d0F2157496aB3c0Af1C",
"constructorArgs": []
}
},
@@ -328,9 +328,9 @@
"name": "AdminFacet",
"fullyQualifiedName": "AdminFacet.sol:AdminFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x2336b4b0272ba66187daa614ce8e0f9a44db10e9f42ebd86ea7f399db963db1d",
+ "txHash": "0xb1d040b4fda20b36b1ec7497566e89c8eb30e4d1fc1797ca17dbae8a72e23c93",
"onChain": {
- "address": "0x33D269f18C833947FE498B68dB97bc8990feaaF7",
+ "address": "0x3eBF592D1c101D100383c41BC1174c7A6B328b5f",
"constructorArgs": []
}
},
@@ -338,9 +338,9 @@
"name": "EntityFacet",
"fullyQualifiedName": "EntityFacet.sol:EntityFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x6606d49f94a7a41937f199eff00c308d36dadeb7db85f72ca40fde520d647d39",
+ "txHash": "0x8f6ce633c11d231d71ac57e10038b0d019a36b0d3fffd91384708c02f57eb3ff",
"onChain": {
- "address": "0x48dF0CfAeF96B6B95B87A94b30536a099a44a1E4",
+ "address": "0xC5642620830C29dCC8ec7D5c922291D93643a08E",
"constructorArgs": []
}
},
@@ -348,9 +348,9 @@
"name": "GovernanceFacet",
"fullyQualifiedName": "GovernanceFacet.sol:GovernanceFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x53598cf0c82937e5fa35db4ad62dce9f261324dc00d428d0fae826a8fd0132d3",
+ "txHash": "0xfa63aa7472dfe142c10534624171669a3633b01d69c33562fd152af1a4f9102b",
"onChain": {
- "address": "0x19D0616218fA67f37E111f5deE950C67261565e7",
+ "address": "0x995929e8dc707F79a89D475740c3c185c187d081",
"constructorArgs": []
}
},
@@ -358,9 +358,9 @@
"name": "MarketFacet",
"fullyQualifiedName": "MarketFacet.sol:MarketFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x36acdc603d548b9273904920cab6708e7f7ccb349cfa7954246175faa453d0c3",
+ "txHash": "0xf5c9d3fbb66f2ab8fafbd6cb7054651dd29aebae2010b824cbf2184fa04296d4",
"onChain": {
- "address": "0x476e8E2491c717cd17f17c06329C481627Cf872A",
+ "address": "0x57987A12d0B69833b2a639603AE1B0B384fd5ebb",
"constructorArgs": []
}
},
@@ -368,9 +368,19 @@
"name": "NaymsOwnershipFacet",
"fullyQualifiedName": "NaymsOwnershipFacet.sol:NaymsOwnershipFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x974369ffe04773472ce0f3ad44656d0abf144574f5db27ae0a2375c3a630522d",
+ "txHash": "0xdc43a5d7a3bfcf38b4064e0b5a257f04d6603861b3e3481228b3a95d52fb58db",
+ "onChain": {
+ "address": "0x985816cD9fEf0A7134DA3B58D449a383d488dB52",
+ "constructorArgs": []
+ }
+ },
+ {
+ "name": "PhasedDiamondCutFacet",
+ "fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
+ "sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
+ "txHash": "0xfaff9fba8c897d6aa812e98370d6ba41b8b5084a3f05c128fa7f00f46d877d78",
"onChain": {
- "address": "0x814307E594990b0baFa158c198172FDabebC25C8",
+ "address": "0x06347Be0f259c5CDe48d9EE1Fd4A30EE7CD6cABF",
"constructorArgs": []
}
},
@@ -378,9 +388,9 @@
"name": "SimplePolicyFacet",
"fullyQualifiedName": "SimplePolicyFacet.sol:SimplePolicyFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x81cf52aba3e7c36501f55bb7324139b973bb05953942c43551692f3faf96beb7",
+ "txHash": "0xb7293ed86b413d98ae5a75ddc46ca43a99503febf8d1117079141af6d3b329b3",
"onChain": {
- "address": "0xd86c71e468Bc79f4070F47ee48BF93068e074C5d",
+ "address": "0x4d1cE83531B0d284D5d74095597E9FC2ab86609a",
"constructorArgs": []
}
},
@@ -388,9 +398,9 @@
"name": "StakingFacet",
"fullyQualifiedName": "StakingFacet.sol:StakingFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x4baf8c94c6f87405d43c8dd785d6b538e4bb83449d1f32c5ad69bcc437cc277e",
+ "txHash": "0x05f11bc3b013401aa817791482dc64980b719aa11fa92244173b3b74c2c7697c",
"onChain": {
- "address": "0xDdA3d3383171eb89b621F5a3679AabE9af6Ce410",
+ "address": "0xe1429C8fdBc8e684CFC6e239DA1bD93778D0C341",
"constructorArgs": []
}
},
@@ -398,39 +408,39 @@
"name": "SystemFacet",
"fullyQualifiedName": "SystemFacet.sol:SystemFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x271223ac639abf078d9efb73ade47ad29695c7533ce258f15b9cd6209f66e2f9",
+ "txHash": "0x35683bd1496eb9de25c562773af70678e4769c619fc72f21b09e02cbc87a745a",
"onChain": {
- "address": "0x8B84541a63bEf23AB3d2C5D99442DC898679cbf4",
+ "address": "0xa19BA1aEF33c5c13b6DF3793393784AA79C9A216",
"constructorArgs": []
}
},
{
- "name": "TokenizedVaultFacet",
- "fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
+ "name": "TokenizedVaultIOFacet",
+ "fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x93cbec5279378fd0380605b4e25ab165c772dba0265507215176712be8486702",
+ "txHash": "0x0466e57a04d11617405ff411db604acd6145811f3f925b1dd0071da602fe3829",
"onChain": {
- "address": "0xf42E7193560134984B9678cCc9774EBCb0310e84",
+ "address": "0x08833B26056232899B8572038B126C5B1e38B39f",
"constructorArgs": []
}
},
{
- "name": "TokenizedVaultIOFacet",
- "fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
+ "name": "UserFacet",
+ "fullyQualifiedName": "UserFacet.sol:UserFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x4beaf986786a0b607251f1449da89ea13f130e1356e60b7ec028a833c2b38f9e",
+ "txHash": "0x9749bac346509e35cab31014643a77d5bd9ef30c7a8832f706eaec97e8b1619d",
"onChain": {
- "address": "0x1f2323EccC25d4F7654C6bE579284D03AF6d9f34",
+ "address": "0x0B246415B03cA0A84Ae847A6395e3bd579aCA70d",
"constructorArgs": []
}
},
{
- "name": "UserFacet",
- "fullyQualifiedName": "UserFacet.sol:UserFacet",
+ "name": "ZapFacet",
+ "fullyQualifiedName": "ZapFacet.sol:ZapFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xc23442d7cc74cad4dae5145de08c567876d6b9008ca89e15a2b7b5f8529c6568",
+ "txHash": "0x7dd40daf251907dda7a5cd0eb5ca9a05a3da1746776ea2a81e78a3795882b235",
"onChain": {
- "address": "0x1d82F910c67f8C0b330D36Df14BE9086aAAB849E",
+ "address": "0xb2928DA4C1F25EEa87ab1d1E288f774CBC193797",
"constructorArgs": []
}
},
@@ -1015,12 +1025,22 @@
"chainId": 84532,
"contracts": [
{
- "name": "StakingFacet",
- "fullyQualifiedName": "StakingFacet.sol:StakingFacet",
+ "name": "TokenizedVaultFacet",
+ "fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
+ "sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
+ "txHash": "0x96e19e3578f39fd6ee33da76138bd8833429268a798030ecdc4f7292dd0bdfe7",
+ "onChain": {
+ "address": "0x88a46FD0c79B62aC708667Bc588403b70Ab685d5",
+ "constructorArgs": []
+ }
+ },
+ {
+ "name": "ACLFacet",
+ "fullyQualifiedName": "ACLFacet.sol:ACLFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x3fb89ec05068933e313d491db53ecdc07f8a908f32a10d9a4d2d2cbd6aa99b8f",
+ "txHash": "0xaea385cf7df8d7b5797a4eb0f0515024ba8c1f48bd2138bca8bed4b5ceca0649",
"onChain": {
- "address": "0x56bA1BC0851DF8EF2d23212b71cF6aFf23de2B4f",
+ "address": "0xb9B266E32971b1fa28CAe98C4CE032ddc9b91860",
"constructorArgs": []
}
},
@@ -1028,9 +1048,9 @@
"name": "AdminFacet",
"fullyQualifiedName": "AdminFacet.sol:AdminFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x077b9ef69ea7a74560fbca0b91a475f7c673df741d25885c10bc426b1604a952",
+ "txHash": "0x11305277c71d310270f42cbfefa6b00adeefdb29064b0a60157aca4da085a11b",
"onChain": {
- "address": "0xD731bdF8b914ff662e8c6B237Fc506D2852795eE",
+ "address": "0xF42d09Eb8ce90134290487ad6501237d51996a9D",
"constructorArgs": []
}
},
@@ -1038,109 +1058,109 @@
"name": "EntityFacet",
"fullyQualifiedName": "EntityFacet.sol:EntityFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x3e027c275ddbd31d50aa211fb59d3003c0a844516479d526f75b99f765f4a660",
+ "txHash": "0x87a3f586b0e7230f92a10e878b025189b09826782064ef3dfb4b490627187a56",
"onChain": {
- "address": "0x9a410d7d81112396f99709a7fb82c7D32dF626A5",
+ "address": "0xddD63991dfB9D6Fe48229495e338Bdef75cbcd93",
"constructorArgs": []
}
},
{
- "name": "MarketFacet",
- "fullyQualifiedName": "MarketFacet.sol:MarketFacet",
+ "name": "GovernanceFacet",
+ "fullyQualifiedName": "GovernanceFacet.sol:GovernanceFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x0d945cd636cf8bd884c6f859b20427d5ea0b6e3563ca72eeefcd5a1c006bbfcb",
+ "txHash": "0x7a3443e6fe25a6ff23b9cca0fa0f69f38845a784fd7fa1a4e64b1c3ca1b894cb",
"onChain": {
- "address": "0x66861b55273184c93d898a8adc38Dc49f9F97Ee2",
+ "address": "0xc17a2295Ffe12C55A49b247cA4a065187D0e7947",
"constructorArgs": []
}
},
{
- "name": "PhasedDiamondCutFacet",
- "fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
+ "name": "MarketFacet",
+ "fullyQualifiedName": "MarketFacet.sol:MarketFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x741acd8976eb2f23fbc46f17d9425da7f0a2bc6992dbfd2ebb63939257f56bfe",
+ "txHash": "0x54230ce83328f621e4650b55097e05d4045168984c5d68e1453da0494d84f83a",
"onChain": {
- "address": "0xB5d8896CEda82FA3523b00cF37e91D1F26e80E0E",
+ "address": "0xFdFeF31eEeB751E23430e668dc32eE76797A0633",
"constructorArgs": []
}
},
{
- "name": "SimplePolicyFacet",
- "fullyQualifiedName": "SimplePolicyFacet.sol:SimplePolicyFacet",
+ "name": "NaymsOwnershipFacet",
+ "fullyQualifiedName": "NaymsOwnershipFacet.sol:NaymsOwnershipFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x3ec3b0cf2182fabbb8425442ebb4b4e328dd709d519f0af2f9ef364eaa641a79",
+ "txHash": "0xdd55e10c600fd3034ec79b11b69b7ff48996f9969769fecc84cdf3fc60af2b53",
"onChain": {
- "address": "0xFAf0C7120dcd00a4235261328869934ccA6ef2d3",
+ "address": "0xf547FE61ae0F09991B0cdc67045aA3af8C416dD3",
"constructorArgs": []
}
},
{
- "name": "SystemFacet",
- "fullyQualifiedName": "SystemFacet.sol:SystemFacet",
+ "name": "SimplePolicyFacet",
+ "fullyQualifiedName": "SimplePolicyFacet.sol:SimplePolicyFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xf94b8b06b7f0ba633b82b8ec1db2e0e1986b9bf97ad734c9a513bd6148d29ade",
+ "txHash": "0x2156d75ffdaf67cde1887b7d37245372d007d8700d2058bfe28896782b9020ce",
"onChain": {
- "address": "0x828210FD60505415FcA766FDE661D696CD6Af02D",
+ "address": "0xB6ddECA657aD25D9739CE61F95BB69FD132a2fa6",
"constructorArgs": []
}
},
{
- "name": "TokenizedVaultFacet",
- "fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
+ "name": "StakingFacet",
+ "fullyQualifiedName": "StakingFacet.sol:StakingFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x31bdb1f258f71d8e8e0fe7697c52a235920b6ed2038fb1798bba3f2c0004082b",
+ "txHash": "0x36360314067dea521d635b4a3c021057f5d659c2b3efe3fba33535a627c9b964",
"onChain": {
- "address": "0xc89B93249738A582Ad92D0faBa94C7BCE04a7a8F",
+ "address": "0xA8E58224a3f440555c9338893dAeA9186F58db5a",
"constructorArgs": []
}
},
{
- "name": "TokenizedVaultIOFacet",
- "fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
+ "name": "SystemFacet",
+ "fullyQualifiedName": "SystemFacet.sol:SystemFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xc5be695eb3c99ca7cf1ad844a9bb1a2f815d6aedb4e66747d7c89b874032d421",
+ "txHash": "0x231a4784a5711337e3e82713f819b3e52573d5983fe5cf388d9ccc8d205a01d0",
"onChain": {
- "address": "0x6EA81F51B6C12cE8b029249c872f0583F099bcDb",
+ "address": "0x1d51A0b14729B188E818eD5A435BEeDe7bA71ACe",
"constructorArgs": []
}
},
{
- "name": "ACLFacet",
- "fullyQualifiedName": "ACLFacet.sol:ACLFacet",
+ "name": "TokenizedVaultIOFacet",
+ "fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x046a509d90cfbdcd4ebc3ecf603c2e0ebb83c09457f17c9e81c9fecc92ef4639",
+ "txHash": "0xadb9ed81fa5ed8293ff6f97f66d2a3a15fb20c73cf864bfc04140db7a7ddb4ce",
"onChain": {
- "address": "0x634095e86FE2E9Ad506D5daebE0b1E22E71252d4",
+ "address": "0xd869BE8195D193101d9bd4711049C1047F03a8c8",
"constructorArgs": []
}
},
{
- "name": "GovernanceFacet",
- "fullyQualifiedName": "GovernanceFacet.sol:GovernanceFacet",
+ "name": "UserFacet",
+ "fullyQualifiedName": "UserFacet.sol:UserFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x802d89ff3c619bb6d5d1b1f3afadb723e28adfcbbe61ad6401303938feabddd5",
+ "txHash": "0x668ced3d0c12c15a40363d0fff119c9c1ac2dddf00ca5982f30ae492035bff19",
"onChain": {
- "address": "0x3286dA55EaeB7Fe68bCDF901fA9685a4CaE264dF",
+ "address": "0x31907a5D8a8BB90ef8203884E9d76774Fa5ad3a8",
"constructorArgs": []
}
},
{
- "name": "NaymsOwnershipFacet",
- "fullyQualifiedName": "NaymsOwnershipFacet.sol:NaymsOwnershipFacet",
+ "name": "ZapFacet",
+ "fullyQualifiedName": "ZapFacet.sol:ZapFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xd06d7131c3897714df6bd45b7dd57618f268b86eb9c2259f8532f45cd9626c36",
+ "txHash": "0x32d70cd5723cf1fe4141cb3e0cd7f23d702e19016684eb3bf7195796a666af03",
"onChain": {
- "address": "0xebeF106aBf7f32fa64Aa9d6C96766A6534CB83d3",
+ "address": "0xCb7d275474B2336Ac9DBDE0a0669a7dfCf299D3e",
"constructorArgs": []
}
},
{
- "name": "UserFacet",
- "fullyQualifiedName": "UserFacet.sol:UserFacet",
+ "name": "PhasedDiamondCutFacet",
+ "fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x42215092959b93afbbb476f79aab950389b01b2c926f2d62c0b98daf5f490744",
+ "txHash": "0x741acd8976eb2f23fbc46f17d9425da7f0a2bc6992dbfd2ebb63939257f56bfe",
"onChain": {
- "address": "0x860C9188bcc6b794BA7EDD06aA5159c6b74bAFeB",
+ "address": "0xB5d8896CEda82FA3523b00cF37e91D1F26e80E0E",
"constructorArgs": []
}
},
@@ -1189,22 +1209,32 @@
"chainId": 1313161555,
"contracts": [
{
- "name": "PhasedDiamondCutFacet",
- "fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
+ "name": "TokenizedVaultFacet",
+ "fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x543a981cd09664ce2430f080f393d7f84f972fba1d828f980a2ec40942bc4096",
+ "txHash": "0xa8f3c326f453eb941477a827d22c87e9cb0ada4a35dcfd65253b6e3f3d0bd1ec",
"onChain": {
- "address": "0x86952dC734702030Eaaede33E070159F7A8B07dc",
+ "address": "0x21702829450ffb9bdBC839063252DEbb87a832d9",
"constructorArgs": []
}
},
{
- "name": "StakingFacet",
- "fullyQualifiedName": "StakingFacet.sol:StakingFacet",
+ "name": "ACLFacet",
+ "fullyQualifiedName": "ACLFacet.sol:ACLFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x61dbca53748b3a729b67160690edec962cbaaeada9795574e4115e103e695b69",
+ "txHash": "0xa425708122492fd66c40f161e6e4dee09187edf733d614fa23ad1545072da4ff",
"onChain": {
- "address": "0x894e7a37f08992C457E522a5610eFc1714935E26",
+ "address": "0x8A38B14aE079581eBaCfC391bBb3D49057953BF4",
+ "constructorArgs": []
+ }
+ },
+ {
+ "name": "AdminFacet",
+ "fullyQualifiedName": "AdminFacet.sol:AdminFacet",
+ "sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
+ "txHash": "0xbbeb4a99f2b5bcb57ab341d6cd8f47fdffeeb879e496b038b05acf4db665cba5",
+ "onChain": {
+ "address": "0x268593c8f62F0D77E86D19697Ee9c9A2063B15A5",
"constructorArgs": []
}
},
@@ -1212,19 +1242,19 @@
"name": "EntityFacet",
"fullyQualifiedName": "EntityFacet.sol:EntityFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x8fb4777c91342976ecc41ef431e4add163dfb1bc01526e67e6f6b9d250fc0759",
+ "txHash": "0x4645a4096e998eb1f91c279701efdba871635538deb7eb73c071ebeeec298073",
"onChain": {
- "address": "0x62AE01F93C7280271616E9Dd3A3789Fc734F8192",
+ "address": "0x1AE9B5974fD8649AE58Fc1FF509fa96892FCb5E8",
"constructorArgs": []
}
},
{
- "name": "ACLFacet",
- "fullyQualifiedName": "ACLFacet.sol:ACLFacet",
+ "name": "GovernanceFacet",
+ "fullyQualifiedName": "GovernanceFacet.sol:GovernanceFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x10d09e97903a93fe989014b3751b78e3ee00c456827365d71957bdd729183e63",
+ "txHash": "0x0537a33a6703375672d01e1429a15b66bd296835cf74ef23a9745becb4c1ff91",
"onChain": {
- "address": "0x24da9216FD050b124Ab77FC5072E9377566Dc598",
+ "address": "0xB71f83ECC8dDf00101626d3FF95f6ee0Df65eaDc",
"constructorArgs": []
}
},
@@ -1232,102 +1262,102 @@
"name": "MarketFacet",
"fullyQualifiedName": "MarketFacet.sol:MarketFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x6cd6f28aa13c1038113ecebf064536db3cfd2ee000a8ead09b307c2dc70f3525",
+ "txHash": "0x297722357cbb3a819dad9168ae246a7a98c4208ba6eeec180554c25ed7fd65d6",
"onChain": {
- "address": "0x1c0e6919d498F3f49f8b7Efdda23C5F7AA56416E",
+ "address": "0x8637F569f6Aeac33EdDBB8DBA97407Ef619AeC37",
"constructorArgs": []
}
},
{
- "name": "SimplePolicyFacet",
- "fullyQualifiedName": "SimplePolicyFacet.sol:SimplePolicyFacet",
+ "name": "NaymsOwnershipFacet",
+ "fullyQualifiedName": "NaymsOwnershipFacet.sol:NaymsOwnershipFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xea5ae74755072167258c1ecd1ce079c31292ffb1b47e01a1886146f778d12096",
+ "txHash": "0x8f0be8bd64149baa4d9e5ab740f9239d15bc81c8cd805eb08d18bb840afa7925",
"onChain": {
- "address": "0xc92124E373b331AA22f8f277651b903aA8e96583",
+ "address": "0x783BAA0E392F9161B47cb8Edd7Ed5C6351D1A39c",
"constructorArgs": []
}
},
{
- "name": "SystemFacet",
- "fullyQualifiedName": "SystemFacet.sol:SystemFacet",
+ "name": "PhasedDiamondCutFacet",
+ "fullyQualifiedName": "PhasedDiamondCutFacet.sol:PhasedDiamondCutFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x762d341ab93a7cff5e28d8ec0f0c59b0ac2262bba5c39659ec7e2ff20e391c57",
+ "txHash": "0xacbc058fb062f6b138d414d5cc2ee3c3904ceaf50509b32fd4dce3a07fc8255f",
"onChain": {
- "address": "0xfcA80872073Db2dE6726b087537D7005C235fCeE",
+ "address": "0x5BaE89a53F7d81476c3A472bb9FD97701de118C4",
"constructorArgs": []
}
},
{
- "name": "AdminFacet",
- "fullyQualifiedName": "AdminFacet.sol:AdminFacet",
+ "name": "SimplePolicyFacet",
+ "fullyQualifiedName": "SimplePolicyFacet.sol:SimplePolicyFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x37ca6c1919aab114c3ecc1f328f061eb9241725ee9354dceaf7cc84418c4edeb",
+ "txHash": "0x71d53672b4b0e3706dc926c08cf31c2dfe2040bfc7d79471e4822dd3d4beea98",
"onChain": {
- "address": "0x06bEc7aAC7cC3Dd6b1B8a3A01856d31127487861",
+ "address": "0xa35cC6271D970f52F80e9325aB74b19C0b823F66",
"constructorArgs": []
}
},
{
- "name": "DiamondProxy",
- "fullyQualifiedName": "DiamondProxy.sol:DiamondProxy",
+ "name": "StakingFacet",
+ "fullyQualifiedName": "StakingFacet.sol:StakingFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x094735b16130b5e29d9f454e65edf86c6970930b18610a90de1c3c2990ffd5ee",
+ "txHash": "0xc0ee96a7c496d6ef067bcc8f5d0811272a7b62a85575429fc178651093c7bd04",
"onChain": {
- "address": "0x4F10acBA59A206a66713380De02F9c09880A822F",
- "constructorArgs": [
- "0x931c3aC09202650148Edb2316e97815f904CF4fa"
- ]
+ "address": "0x8F149BF305bb363e7Bd1f46116360c37821dd903",
+ "constructorArgs": []
}
},
{
- "name": "GovernanceFacet",
- "fullyQualifiedName": "GovernanceFacet.sol:GovernanceFacet",
+ "name": "SystemFacet",
+ "fullyQualifiedName": "SystemFacet.sol:SystemFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xb7dde44e8270ee979433ef029a3d3251b9ec45e98db76179cd0978daa27ae6ea",
+ "txHash": "0xd55aee4737b019e21838d8883f14401456f495f7efae818bc0c3f51b250c5e49",
"onChain": {
- "address": "0xfADB5dB5d08BBf950256f79C3665CE8d71d8710f",
+ "address": "0x6CeAa2E66E4b068f75D955bA18f9A0f72e5d532C",
"constructorArgs": []
}
},
{
- "name": "NaymsOwnershipFacet",
- "fullyQualifiedName": "NaymsOwnershipFacet.sol:NaymsOwnershipFacet",
+ "name": "TokenizedVaultIOFacet",
+ "fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x850219af7cb60b2f1d6a004909c74ed0e08e6e287cde81f3c288aeb54b189020",
+ "txHash": "0x1d23d86e9eaef991952bdad3cfaefe9e68610419e084658163b67bf382aedcc8",
"onChain": {
- "address": "0x2F64b09a41b400a58D4485f08f6BCF14D944Ad6f",
+ "address": "0xc8c80Db133edD26554F73648a04af70F5241dBf4",
"constructorArgs": []
}
},
{
- "name": "TokenizedVaultFacet",
- "fullyQualifiedName": "TokenizedVaultFacet.sol:TokenizedVaultFacet",
+ "name": "UserFacet",
+ "fullyQualifiedName": "UserFacet.sol:UserFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xa63377a28b7a6d0e20d089bbbbfc8b860df9e4781e490ad72e94517a08f52772",
+ "txHash": "0x14e0733e38ec3f72d338a179f6d202585f324fa875547b0a2ee659de894d9d6d",
"onChain": {
- "address": "0x002d2970F2AacFD2344d2C1cc35b3985A374A73C",
+ "address": "0x7FeF514DE762015229B51080c75d1C65016f38b4",
"constructorArgs": []
}
},
{
- "name": "TokenizedVaultIOFacet",
- "fullyQualifiedName": "TokenizedVaultIOFacet.sol:TokenizedVaultIOFacet",
+ "name": "ZapFacet",
+ "fullyQualifiedName": "ZapFacet.sol:ZapFacet",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0xb7d4d93ef9274cf2584c048a971ba00a2bf6286fc6ade61b33187b090ce23051",
+ "txHash": "0x9c838a364cc350f1af090822fd79d0948499a6724ef525a3b71e6df334589de0",
"onChain": {
- "address": "0x23c9080C0A5236C6E6297fB1f4184C9f200a1A80",
+ "address": "0x3d28d86602469CA216d05937D098C610e37EE25b",
"constructorArgs": []
}
},
{
- "name": "UserFacet",
- "fullyQualifiedName": "UserFacet.sol:UserFacet",
+ "name": "DiamondProxy",
+ "fullyQualifiedName": "DiamondProxy.sol:DiamondProxy",
"sender": "0x931c3aC09202650148Edb2316e97815f904CF4fa",
- "txHash": "0x6160fb1fb7657cde4d9c42e6b4853bc7f4b9eee248c0aa2df1e926079b8cc194",
+ "txHash": "0x094735b16130b5e29d9f454e65edf86c6970930b18610a90de1c3c2990ffd5ee",
"onChain": {
- "address": "0x909677eBF6e09B669dBe01950E9F3FfCe7602097",
- "constructorArgs": []
+ "address": "0x4F10acBA59A206a66713380De02F9c09880A822F",
+ "constructorArgs": [
+ "0x931c3aC09202650148Edb2316e97815f904CF4fa"
+ ]
}
},
{
diff --git a/lib/solidity-lib b/lib/solidity-lib
deleted file mode 160000
index c01640b0..00000000
--- a/lib/solidity-lib
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit c01640b0f0f1d8a85cba8de378cc48469fcfd9a6
diff --git a/lib/solidity-stringutils b/lib/solidity-stringutils
deleted file mode 160000
index 4b2fcc43..00000000
--- a/lib/solidity-stringutils
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4b2fcc43fa0426e19ce88b1f1ec16f5903a2e461
diff --git a/package.json b/package.json
index 30053bd5..6294300f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@nayms/contracts",
- "version": "3.9.2",
+ "version": "3.9.7",
"main": "index.js",
"repository": "https://github.com/nayms/contracts-v3.git",
"author": "Kevin Park ",
@@ -30,12 +30,12 @@
"solhint:check": "solhint --config ./.solhint.json 'src/**/*.sol'",
"lint": "yarn prettier && yarn run solhint",
"lint:check": "yarn prettier:check && yarn run solhint:check",
- "docgen": "rm -rf docs/facets/*Facet.md && node cli-tools/docgen.js",
"anvil": "anvil --host 0.0.0.0 --chain-id 31337 --accounts 30 -m ./nayms_mnemonic.txt",
"build": "gemforge build",
"deploy": "./script/gemforge/deploy.js",
"query": "gemforge query",
- "test": "forge test --no-match-test testReplaceDiamondCut"
+ "verify": "gemforge verify",
+ "test": "forge test --no-match-test testFork"
},
"devDependencies": {
"chalk": "4",
diff --git a/remappings.txt b/remappings.txt
index c72a2f36..e8608106 100644
--- a/remappings.txt
+++ b/remappings.txt
@@ -1,10 +1,7 @@
forge-std/=lib/forge-std/src/
+ds-test/=lib/ds-test/src/
@openzeppelin/contracts/=lib/oz/contracts/
diamond-2-hardhat/=lib/diamond-2-hardhat/contracts/
-ds-test/=lib/ds-test/src/
erc4626-tests/=lib/oz/lib/erc4626-tests/
-oz/=lib/oz/
solady/=lib/solady/src/
-solidity-lib/=lib/solidity-lib/contracts/
-solidity-stringutils/=lib/solidity-stringutils/
solmate/=lib/solmate/src/
diff --git a/script/gemforge/verify.js b/script/gemforge/verify.js
deleted file mode 100755
index 55b92ffe..00000000
--- a/script/gemforge/verify.js
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env node
-
-const chalk = require("chalk");
-
-(async () => {
- require("dotenv").config();
- const { $ } = await import("execa");
-
- const deploymentInfo = require("../../gemforge.deployments.json");
- const gemforgeConfig = require("../../gemforge.config.cjs");
-
- const target = process.env.GEMFORGE_DEPLOY_TARGET;
- if (!target) {
- throw new Error("GEMFORGE_DEPLOY_TARGET env var not set");
- }
-
- // skip for localhost and forks
- if (target === "local" || /fork/i.test(target)) {
- console.log("Skipping contract verification on", target);
- return;
- }
-
- const contracts = deploymentInfo[target]?.contracts || [];
- const verifiers = gemforgeConfig.networks?.[target]?.verifiers || [{ verifierName: "etherscan" }];
-
- for (const { verifierName, verifierUrl, verifierApiKey } of verifiers) {
- console.log(chalk.cyan(`Verifying ${target} target on ${verifierName}`));
-
- const apiKey = verifierApiKey || process.env.ETHERSCAN_API_KEY;
- const verificationArg = verifierUrl ? `--verifier-url=${verifierUrl}` : `--verifier=${verifierName}`;
-
- for (const { name, onChain } of contracts) {
- let args = "0x";
-
- if (onChain.constructorArgs.length) {
- args = (await $`cast abi-encode constructor(address) ${onChain.constructorArgs.join(" ")}`).stdout;
- }
-
- console.log(`Verifying ${name} at ${onChain.address} with args ${args}`);
-
- await $`forge v ${onChain.address} ${name} --constructor-args ${args} --chain-id ${deploymentInfo[target].chainId} ${verificationArg} --etherscan-api-key ${apiKey} --watch`;
-
- console.log(chalk.green(` Verified!`));
- }
- }
-})();
diff --git a/src/facets/AdminFacet.sol b/src/facets/AdminFacet.sol
index 6cea920b..43682575 100644
--- a/src/facets/AdminFacet.sol
+++ b/src/facets/AdminFacet.sol
@@ -2,6 +2,7 @@
pragma solidity 0.8.20;
import { AppStorage, LibAppStorage } from "../shared/AppStorage.sol";
+import { OnboardingApproval } from "../shared/FreeStructs.sol";
import { Modifiers } from "../shared/Modifiers.sol";
import { LibAdmin } from "../libs/LibAdmin.sol";
import { LibObject } from "../libs/LibObject.sol";
@@ -148,25 +149,21 @@ contract AdminFacet is Modifiers {
}
/**
- * @notice Approve a user address for self-onboarding
- * @param _userAddress user account address
+ * @notice Create a token holder entity for a user account
+ * @param _onboardingApproval onboarding approval parameters, includes user address, entity ID and role ID
*/
- function approveSelfOnboarding(address _userAddress, bytes32 _entityId, bytes32 _roleId) external assertPrivilege(LibAdmin._getSystemId(), LC.GROUP_ONBOARDING_APPROVERS) {
- LibAdmin._approveSelfOnboarding(_userAddress, _entityId, _roleId);
+ function onboardViaSignature(OnboardingApproval calldata _onboardingApproval) external {
+ LibAdmin._onboardUserViaSignature(_onboardingApproval);
}
/**
- * @notice Create a token holder entity for a user account
+ * @notice Hash to be signed by the onboarding approver
+ * @param _userAddress Address being approved to onboard
+ * @param _entityId Entity ID being approved for onboarding
+ * @param _roleId Role being apprved for onboarding
+ * @return Onboarding approval digest
*/
- function onboard() external {
- LibAdmin._onboardUser(msg.sender);
- }
-
- function isSelfOnboardingApproved(address _userAddress, bytes32 _entityId, bytes32 _roleId) external view returns (bool) {
- return LibAdmin._isSelfOnboardingApproved(_userAddress, _entityId, _roleId);
- }
-
- function cancelSelfOnboarding(address _user) external assertPrivilege(LibAdmin._getSystemId(), LC.GROUP_SYSTEM_MANAGERS) {
- LibAdmin._cancelSelfOnboarding(_user);
+ function getOnboardingHash(address _userAddress, bytes32 _entityId, bytes32 _roleId) external view returns (bytes32) {
+ return LibAdmin._getOnboardingHash(_userAddress, _entityId, _roleId);
}
}
diff --git a/src/facets/StakingFacet.sol b/src/facets/StakingFacet.sol
index 5dce2f11..af94e7b0 100644
--- a/src/facets/StakingFacet.sol
+++ b/src/facets/StakingFacet.sol
@@ -134,6 +134,13 @@ contract StakingFacet is Modifiers {
LibTokenizedVaultStaking._collectRewards(parentId, _entityId, lastPaid);
}
+ function compoundRewards(bytes32 _entityId) external notLocked {
+ bytes32 parentId = LibObject._getParent(msg.sender._getIdForAddress());
+ uint64 lastPaid = LibTokenizedVaultStaking._lastPaidInterval(_entityId);
+
+ LibTokenizedVaultStaking._compoundRewards(parentId, _entityId, lastPaid);
+ }
+
/**
* @notice Collect rewards for a staker
* @param _entityId staking entity ID
diff --git a/src/facets/TokenizedVaultFacet.sol b/src/facets/TokenizedVaultFacet.sol
index 87a5693b..63dcd6ba 100644
--- a/src/facets/TokenizedVaultFacet.sol
+++ b/src/facets/TokenizedVaultFacet.sol
@@ -140,6 +140,16 @@ contract TokenizedVaultFacet is Modifiers, ReentrancyGuard {
amount = LibTokenizedVault._getLockedBalance(_entityId, _tokenId);
}
+ /**
+ * @notice Get the amount of tokens that an entity has available (deposited, but not locked)
+ * @param _entityId Unique platform ID of the entity.
+ * @param _tokenId The ID assigned to an external token.
+ * @return amount of tokens that the entity has available
+ */
+ function getAvailableBalance(bytes32 _entityId, bytes32 _tokenId) external view returns (uint256) {
+ return LibTokenizedVault._getAvailableBalance(_entityId, _tokenId);
+ }
+
/**
* @notice A system admin can transfer funds from an ID to another one.
*
diff --git a/src/facets/TokenizedVaultIOFacet.sol b/src/facets/TokenizedVaultIOFacet.sol
index 2306116a..b83d0165 100644
--- a/src/facets/TokenizedVaultIOFacet.sol
+++ b/src/facets/TokenizedVaultIOFacet.sol
@@ -9,7 +9,7 @@ import { LibObject } from "../libs/LibObject.sol";
import { LibConstants as LC } from "../libs/LibConstants.sol";
import { LibACL } from "../libs/LibACL.sol";
import { LibHelpers } from "../libs/LibHelpers.sol";
-import { ExternalWithdrawInvalidReceiver } from "../shared/CustomErrors.sol";
+import { ExternalWithdrawInvalidReceiver, InvalidERC20Token } from "../shared/CustomErrors.sol";
import { ReentrancyGuard } from "../utils/ReentrancyGuard.sol";
/**
@@ -29,8 +29,10 @@ contract TokenizedVaultIOFacet is Modifiers, ReentrancyGuard {
address _externalTokenAddress,
uint256 _amount
) external notLocked nonReentrant assertPrivilege(LibObject._getParentFromAddress(msg.sender), LC.GROUP_EXTERNAL_DEPOSIT) {
- // a user can only deposit an approved external ERC20 token
- require(LibAdmin._isSupportedExternalTokenAddress(_externalTokenAddress), "extDeposit: invalid ERC20 token");
+ if (!LibAdmin._isSupportedExternalTokenAddress(_externalTokenAddress)) {
+ revert InvalidERC20Token(_externalTokenAddress, "extDeposit");
+ }
+
// a user can only deposit to their valid entity
bytes32 entityId = LibObject._getParentFromAddress(msg.sender);
require(LibEntity._isEntity(entityId), "extDeposit: invalid receiver");
diff --git a/src/facets/ZapFacet.sol b/src/facets/ZapFacet.sol
new file mode 100644
index 00000000..3a422759
--- /dev/null
+++ b/src/facets/ZapFacet.sol
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.20;
+
+import { PermitSignature, OnboardingApproval } from "../shared/FreeStructs.sol";
+import { InvalidERC20Token } from "../shared/CustomErrors.sol";
+import { Modifiers } from "../shared/Modifiers.sol";
+import { LibTokenizedVaultIO } from "../libs/LibTokenizedVaultIO.sol";
+import { LibACL } from "../libs/LibACL.sol";
+import { LibAdmin } from "../libs/LibAdmin.sol";
+import { LibObject } from "../libs/LibObject.sol";
+import { LibHelpers } from "../libs/LibHelpers.sol";
+import { LibConstants as LC } from "../libs/LibConstants.sol";
+import { ReentrancyGuard } from "../utils/ReentrancyGuard.sol";
+import { LibTokenizedVaultStaking } from "../libs/LibTokenizedVaultStaking.sol";
+import { IERC20 } from "../interfaces/IERC20.sol";
+import { LibMarket } from "../libs/LibMarket.sol";
+
+contract ZapFacet is Modifiers, ReentrancyGuard {
+ /**
+ * @notice Deposit and stake funds into msg.sender's Nayms platform entity in one transaction using permit
+ * @dev Uses permit to approve token transfer, deposits from msg.sender to their associated entity, and stakes the amount
+ * @param _externalTokenAddress Token address
+ * @param _stakingEntityId Staking entity ID
+ * @param _amountToDeposit Deposit amount
+ * @param _amountToStake Stake amount
+ * @param _permitSignature The permit signature parameters
+ * @param _onboardingApproval The onboarding approval parameters
+ */
+ function zapStake(
+ address _externalTokenAddress,
+ bytes32 _stakingEntityId,
+ uint256 _amountToDeposit,
+ uint256 _amountToStake,
+ PermitSignature calldata _permitSignature,
+ OnboardingApproval calldata _onboardingApproval
+ ) external notLocked nonReentrant {
+ if (!LibAdmin._isSupportedExternalTokenAddress(_externalTokenAddress)) {
+ revert InvalidERC20Token(_externalTokenAddress, "zapStake");
+ }
+
+ bytes32 parentId = LibObject._getParentFromAddress(msg.sender);
+
+ if (_onboardingApproval.entityId != 0 && parentId != _onboardingApproval.entityId) {
+ LibAdmin._onboardUserViaSignature(_onboardingApproval);
+ }
+
+ // Use permit to set allowance
+ IERC20(_externalTokenAddress).permit(msg.sender, address(this), _amountToDeposit, _permitSignature.deadline, _permitSignature.v, _permitSignature.r, _permitSignature.s);
+
+ // Perform the deposit
+ LibTokenizedVaultIO._externalDeposit(parentId, _externalTokenAddress, _amountToDeposit);
+
+ // Stake the deposited amount
+ LibTokenizedVaultStaking._stake(parentId, _stakingEntityId, _amountToStake);
+ }
+
+ /**
+ * @notice Deposit tokens and execute a limit order in one transaction using permit
+ * @dev Uses permit to approve token transfer and performs external deposit and limit order execution
+ * @param _externalTokenAddress Token address
+ * @param _depositAmount Amount to deposit
+ * @param _sellToken Sell token ID
+ * @param _sellAmount Sell amount
+ * @param _buyToken Buy token ID
+ * @param _buyAmount Buy amount
+ * @param _permitSignature The permit signature parameters
+ * @param _onboardingApproval The onboarding approval parameters
+ */
+ function zapOrder(
+ address _externalTokenAddress,
+ uint256 _depositAmount,
+ bytes32 _sellToken,
+ uint256 _sellAmount,
+ bytes32 _buyToken,
+ uint256 _buyAmount,
+ PermitSignature calldata _permitSignature,
+ OnboardingApproval calldata _onboardingApproval
+ ) external notLocked nonReentrant {
+ if (!LibAdmin._isSupportedExternalTokenAddress(_externalTokenAddress)) {
+ revert InvalidERC20Token(_externalTokenAddress, "zapOrder");
+ }
+
+ bytes32 parentId = _onboardingApproval.entityId;
+
+ bool isOnboardingCP = _onboardingApproval.roleId == LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
+ bool isCurrentlyCP = LibACL._isInGroup(parentId, LibHelpers._stringToBytes32(LC.SYSTEM_IDENTIFIER), LibHelpers._stringToBytes32(LC.GROUP_CAPITAL_PROVIDERS));
+
+ if (!isCurrentlyCP && isOnboardingCP) {
+ LibAdmin._onboardUserViaSignature(_onboardingApproval);
+ }
+
+ LibACL._assertPriviledge(parentId, LC.GROUP_EXECUTE_LIMIT_OFFER);
+
+ // Use permit to set allowance
+ IERC20(_externalTokenAddress).permit(msg.sender, address(this), _depositAmount, _permitSignature.deadline, _permitSignature.v, _permitSignature.r, _permitSignature.s);
+
+ // Perform the external deposit
+ LibTokenizedVaultIO._externalDeposit(parentId, _externalTokenAddress, _depositAmount);
+
+ // Execute the limit order
+ LibMarket._executeLimitOffer(parentId, _sellToken, _sellAmount, _buyToken, _buyAmount, LC.FEE_TYPE_TRADING);
+ }
+}
diff --git a/src/libs/LibACL.sol b/src/libs/LibACL.sol
index 987cb295..07a3af74 100644
--- a/src/libs/LibACL.sol
+++ b/src/libs/LibACL.sol
@@ -7,10 +7,14 @@ import { LibHelpers } from "./LibHelpers.sol";
import { LibAdmin } from "./LibAdmin.sol";
import { LibObject } from "./LibObject.sol";
import { LibConstants } from "./LibConstants.sol";
-import { CannotUnassignRoleFromSelf, OwnerCannotBeSystemAdmin, RoleIsMissing, AssignerGroupIsMissing } from "../shared/CustomErrors.sol";
+
+import { CannotUnassignRoleFromSelf, OwnerCannotBeSystemAdmin, RoleIsMissing, AssignerGroupIsMissing, InvalidGroupPrivilege } from "../shared/CustomErrors.sol";
+
+import { LibString } from "solady/utils/LibString.sol";
library LibACL {
- using LibHelpers for bytes32;
+ using LibString for *;
+ using LibHelpers for *;
/**
* @dev Emitted when a role gets updated. Empty roleId is assigned upon role removal
@@ -129,6 +133,18 @@ library LibACL {
return false;
}
+ function _assertPriviledge(bytes32 _context, string memory _group) internal view {
+ if (!_hasGroupPrivilege(LibHelpers._getIdForAddress(msg.sender), _context, LibHelpers._stringToBytes32(_group)))
+ /// Note: If the role returned by `_getRoleInContext` is empty (represented by bytes32(0)), we explicitly return an empty string.
+ /// This ensures the user doesn't receive a string that could potentially include unwanted data (like pointer and length) without any meaningful content.
+ revert InvalidGroupPrivilege(
+ msg.sender._getIdForAddress(),
+ _context,
+ (_getRoleInContext(msg.sender._getIdForAddress(), _context) == bytes32(0)) ? "" : _getRoleInContext(msg.sender._getIdForAddress(), _context).fromSmallString(),
+ _group
+ );
+ }
+
function _getRoleInContext(bytes32 _objectId, bytes32 _contextId) internal view returns (bytes32) {
AppStorage storage s = LibAppStorage.diamondStorage();
return s.roles[_objectId][_contextId];
diff --git a/src/libs/LibAdmin.sol b/src/libs/LibAdmin.sol
index 961fa2b1..f64d79a1 100644
--- a/src/libs/LibAdmin.sol
+++ b/src/libs/LibAdmin.sol
@@ -2,38 +2,40 @@
pragma solidity 0.8.20;
import { AppStorage, FunctionLockedStorage, LibAppStorage } from "../shared/AppStorage.sol";
-import { Entity, EntityApproval } from "../shared/FreeStructs.sol";
+import { Entity, OnboardingApproval } from "../shared/FreeStructs.sol";
import { LibConstants as LC } from "./LibConstants.sol";
import { LibHelpers } from "./LibHelpers.sol";
import { LibObject } from "./LibObject.sol";
import { LibERC20 } from "./LibERC20.sol";
import { LibEntity } from "./LibEntity.sol";
import { LibACL } from "./LibACL.sol";
+import { LibEIP712 } from "./LibEIP712.sol";
+
+import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
+import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
+
// prettier-ignore
import {
- CannotAddNullDiscountToken,
- CannotAddNullSupportedExternalToken,
CannotSupportExternalTokenWithMoreThan18Decimals,
ObjectTokenSymbolAlreadyInUse,
MinimumSellCannotBeZero,
- EntityExistsAlready,
- EntityOnboardingAlreadyApproved,
EntityOnboardingNotApproved,
InvalidSelfOnboardRoleApproval,
- InvalidEntityId
+ InvalidSignatureError,
+ InvalidSignatureSError,
+ InvalidSignatureLength
} from "../shared/CustomErrors.sol";
import { IDiamondProxy } from "src/generated/IDiamondProxy.sol";
library LibAdmin {
+ using ECDSA for bytes32;
+
event MaxDividendDenominationsUpdated(uint8 oldMax, uint8 newMax);
event SupportedTokenAdded(address indexed tokenAddress);
event FunctionsLocked(bytes4[] functionSelectors);
event FunctionsUnlocked(bytes4[] functionSelectors);
- event ObjectMinimumSellUpdated(bytes32 objectId, uint256 newMinimumSell);
- event SelfOnboardingApproved(address indexed userAddress);
event SelfOnboardingCompleted(address indexed userAddress);
- event SelfOnboardingCancelled(address indexed userAddress);
/// @notice The minimum amount of an object (par token, external token) that can be sold on the market
event MinimumSellUpdated(bytes32 objectId, uint256 minimumSell);
@@ -146,8 +148,11 @@ library LibAdmin {
s.locked[IDiamondProxy.cancelSimplePolicy.selector] = true;
s.locked[IDiamondProxy.createSimplePolicy.selector] = true;
s.locked[IDiamondProxy.createEntity.selector] = true;
+ s.locked[IDiamondProxy.compoundRewards.selector] = true;
+ s.locked[IDiamondProxy.zapStake.selector] = true;
+ s.locked[IDiamondProxy.zapOrder.selector] = true;
- bytes4[] memory lockedFunctions = new bytes4[](22);
+ bytes4[] memory lockedFunctions = new bytes4[](25);
lockedFunctions[0] = IDiamondProxy.startTokenSale.selector;
lockedFunctions[1] = IDiamondProxy.paySimpleClaim.selector;
lockedFunctions[2] = IDiamondProxy.paySimplePremium.selector;
@@ -170,6 +175,9 @@ library LibAdmin {
lockedFunctions[19] = IDiamondProxy.createSimplePolicy.selector;
lockedFunctions[20] = IDiamondProxy.createEntity.selector;
lockedFunctions[21] = IDiamondProxy.collectRewardsToInterval.selector;
+ lockedFunctions[22] = IDiamondProxy.compoundRewards.selector;
+ lockedFunctions[23] = IDiamondProxy.zapStake.selector;
+ lockedFunctions[24] = IDiamondProxy.zapOrder.selector;
emit FunctionsLocked(lockedFunctions);
}
@@ -198,8 +206,11 @@ library LibAdmin {
s.locked[IDiamondProxy.createSimplePolicy.selector] = false;
s.locked[IDiamondProxy.createEntity.selector] = false;
s.locked[IDiamondProxy.collectRewardsToInterval.selector] = false;
+ s.locked[IDiamondProxy.compoundRewards.selector] = false;
+ s.locked[IDiamondProxy.zapStake.selector] = false;
+ s.locked[IDiamondProxy.zapOrder.selector] = false;
- bytes4[] memory lockedFunctions = new bytes4[](22);
+ bytes4[] memory lockedFunctions = new bytes4[](25);
lockedFunctions[0] = IDiamondProxy.startTokenSale.selector;
lockedFunctions[1] = IDiamondProxy.paySimpleClaim.selector;
lockedFunctions[2] = IDiamondProxy.paySimplePremium.selector;
@@ -222,87 +233,100 @@ library LibAdmin {
lockedFunctions[19] = IDiamondProxy.createSimplePolicy.selector;
lockedFunctions[20] = IDiamondProxy.createEntity.selector;
lockedFunctions[21] = IDiamondProxy.collectRewardsToInterval.selector;
+ lockedFunctions[22] = IDiamondProxy.compoundRewards.selector;
+ lockedFunctions[23] = IDiamondProxy.zapStake.selector;
+ lockedFunctions[24] = IDiamondProxy.zapOrder.selector;
emit FunctionsUnlocked(lockedFunctions);
}
- function _approveSelfOnboarding(address _userAddress, bytes32 _entityId, bytes32 _roleId) internal {
+ function _onboardUserViaSignature(OnboardingApproval memory _approval) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
- // The entityId must be the valid type (entity).
- if (!LibObject._isObjectType(_entityId, LC.OBJECT_TYPE_ENTITY)) revert InvalidEntityId(_entityId);
+ address userAddress = msg.sender;
+
+ bytes32 entityId = _approval.entityId;
+ bytes32 roleId = _approval.roleId;
+ bytes memory sig = _approval.signature;
- // Require that the user is not approved for the role already
- if (_isSelfOnboardingApproved(_userAddress, _entityId, _roleId)) revert EntityOnboardingAlreadyApproved(_userAddress);
+ if (entityId == 0 || roleId == 0 || sig.length == 0) revert EntityOnboardingNotApproved(userAddress);
- bool isTokenHolder = _roleId == LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
- bool isCapitalProvider = _roleId == LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
+ bool isTokenHolder = roleId == LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
+ bool isCapitalProvider = roleId == LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
if (!isTokenHolder && !isCapitalProvider) {
- revert InvalidSelfOnboardRoleApproval(_roleId);
+ revert InvalidSelfOnboardRoleApproval(roleId);
}
- s.selfOnboarding[_userAddress] = EntityApproval({ entityId: _entityId, roleId: _roleId });
-
- emit SelfOnboardingApproved(_userAddress);
- }
-
- function _onboardUser(address _userAddress) internal {
- AppStorage storage s = LibAppStorage.diamondStorage();
- EntityApproval memory approval = s.selfOnboarding[_userAddress];
+ bytes32 signingHash = _getOnboardingHash(userAddress, entityId, roleId);
+ bytes32 signerId = LibHelpers._getIdForAddress(_getSigner(signingHash, sig));
- if (approval.entityId == 0 || approval.roleId == 0) {
- revert EntityOnboardingNotApproved(_userAddress);
+ if (!LibACL._isInGroup(signerId, LibAdmin._getSystemId(), LibHelpers._stringToBytes32(LC.GROUP_ONBOARDING_APPROVERS))) {
+ revert EntityOnboardingNotApproved(userAddress);
}
- bytes32 userId = LibHelpers._getIdForAddress(_userAddress);
-
- if (!s.existingEntities[approval.entityId]) {
+ if (!s.existingEntities[entityId]) {
Entity memory entity;
- LibEntity._createEntity(approval.entityId, userId, entity, 0);
- }
-
- if (s.roles[approval.entityId][approval.entityId] != 0) {
- LibACL._unassignRole(approval.entityId, approval.entityId);
+ bytes32 userId = LibHelpers._getIdForAddress(userAddress);
+ LibEntity._createEntity(entityId, userId, entity, 0);
}
- if (s.roles[approval.entityId][LibAdmin._getSystemId()] != 0) {
- LibACL._unassignRole(approval.entityId, LibAdmin._getSystemId());
+ if (s.roles[entityId][LibAdmin._getSystemId()] != 0) {
+ LibACL._unassignRole(entityId, LibAdmin._getSystemId());
}
+ LibACL._assignRole(entityId, LibAdmin._getSystemId(), roleId);
- LibACL._assignRole(approval.entityId, LibAdmin._getSystemId(), approval.roleId);
- LibACL._assignRole(approval.entityId, approval.entityId, approval.roleId);
-
- delete s.selfOnboarding[_userAddress];
-
- emit SelfOnboardingCompleted(_userAddress);
+ emit SelfOnboardingCompleted(userAddress);
}
- function _isSelfOnboardingApproved(address _userAddress, bytes32 _entityId, bytes32 _roleId) internal view returns (bool) {
+ function _setMinimumSell(bytes32 _objectId, uint256 _minimumSell) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
+ if (_minimumSell == 0) revert MinimumSellCannotBeZero();
- EntityApproval memory approval = s.selfOnboarding[_userAddress];
+ s.objectMinimumSell[_objectId] = _minimumSell;
- return approval.entityId == _entityId && approval.roleId == _roleId;
+ emit MinimumSellUpdated(_objectId, _minimumSell);
}
- function _cancelSelfOnboarding(address _userAddress) internal {
- AppStorage storage s = LibAppStorage.diamondStorage();
+ function _getOnboardingHash(address _userAddress, bytes32 _entityId, bytes32 _roleId) internal view returns (bytes32) {
+ return
+ LibEIP712._hashTypedDataV4(
+ keccak256(abi.encode(keccak256("OnboardingApproval(address _userAddress,bytes32 _entityId,bytes32 _roleId)"), _userAddress, _entityId, _roleId))
+ );
+ }
- if (s.selfOnboarding[_userAddress].entityId == 0 && s.selfOnboarding[_userAddress].roleId == 0) {
- revert EntityOnboardingNotApproved(_userAddress);
- }
+ function _getSigner(bytes32 signingHash, bytes memory signature) internal pure returns (address) {
+ bytes32 r;
+ bytes32 s;
+ uint8 v;
- delete s.selfOnboarding[_userAddress];
+ // ecrecover takes the signature parameters, and the only way to get them
+ if (signature.length != 65) {
+ revert InvalidSignatureLength();
+ }
- emit SelfOnboardingCancelled(_userAddress);
- }
+ // currently is to use assembly.
+ /// @solidity memory-safe-assembly
+ assembly {
+ r := mload(add(signature, 0x20))
+ s := mload(add(signature, 0x40))
+ v := byte(0, mload(add(signature, 0x60)))
+
+ switch v
+ // if v == 0, then v = 27
+ case 0 {
+ v := 27
+ }
+ // if v == 1, then v = 28
+ case 1 {
+ v := 28
+ }
+ }
- function _setMinimumSell(bytes32 _objectId, uint256 _minimumSell) internal {
- AppStorage storage s = LibAppStorage.diamondStorage();
- if (_minimumSell == 0) revert MinimumSellCannotBeZero();
+ (address signer, ECDSA.RecoverError err, ) = ECDSA.tryRecover(MessageHashUtils.toEthSignedMessageHash(signingHash), v, r, s);
- s.objectMinimumSell[_objectId] = _minimumSell;
+ if (err == ECDSA.RecoverError.InvalidSignature) revert InvalidSignatureError(signingHash);
+ else if (err == ECDSA.RecoverError.InvalidSignatureS) revert InvalidSignatureSError(s);
- emit MinimumSellUpdated(_objectId, _minimumSell);
+ return signer;
}
}
diff --git a/src/libs/LibConstants.sol b/src/libs/LibConstants.sol
index f510f12b..f181be43 100644
--- a/src/libs/LibConstants.sol
+++ b/src/libs/LibConstants.sol
@@ -50,19 +50,18 @@ library LibConstants {
string internal constant ROLE_ENTITY_COMPTROLLER_CLAIM = "Comptroller Claim";
string internal constant ROLE_ENTITY_COMPTROLLER_DIVIDEND = "Comptroller Dividend";
- /// old roles
+ string internal constant ROLE_ONBOARDING_APPROVER = "Onboarding Approver";
+ /// old roles
string internal constant ROLE_SPONSOR = "Sponsor";
string internal constant ROLE_CAPITAL_PROVIDER = "Capital Provider";
string internal constant ROLE_INSURED_PARTY = "Insured";
string internal constant ROLE_BROKER = "Broker";
string internal constant ROLE_SERVICE_PROVIDER = "Service Provider";
-
string internal constant ROLE_UNDERWRITER = "Underwriter";
string internal constant ROLE_CLAIMS_ADMIN = "Claims Admin";
string internal constant ROLE_TRADER = "Trader";
string internal constant ROLE_SEGREGATED_ACCOUNT = "Segregated Account";
- string internal constant ROLE_ONBOARDING_APPROVER = "Onboarding Approver";
/// Groups
diff --git a/src/libs/LibHelpers.sol b/src/libs/LibHelpers.sol
index d3cff670..a8b7c9d4 100644
--- a/src/libs/LibHelpers.sol
+++ b/src/libs/LibHelpers.sol
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
-import { LibConstants as LC } from "./LibConstants.sol";
-
/// @notice Pure functions
library LibHelpers {
function _getIdForAddress(address _addr) internal pure returns (bytes32) {
diff --git a/src/libs/LibInitDiamond.sol b/src/libs/LibInitDiamond.sol
index 5e6ae5f1..45a641fb 100644
--- a/src/libs/LibInitDiamond.sol
+++ b/src/libs/LibInitDiamond.sol
@@ -38,6 +38,7 @@ library LibInitDiamond {
LibACL._updateRoleGroup(LC.ROLE_SYSTEM_ADMIN, LC.GROUP_SYSTEM_ADMINS, true);
LibACL._updateRoleGroup(LC.ROLE_SYSTEM_MANAGER, LC.GROUP_START_TOKEN_SALE, true);
LibACL._updateRoleGroup(LC.ROLE_ENTITY_MANAGER, LC.GROUP_START_TOKEN_SALE, true);
+ LibACL._updateRoleGroup(LC.ROLE_ENTITY_ADMIN, LC.GROUP_CANCEL_OFFER, true);
LibACL._updateRoleGroup(LC.ROLE_ENTITY_MANAGER, LC.GROUP_CANCEL_OFFER, true);
LibACL._updateRoleGroup(LC.ROLE_ENTITY_CP, LC.GROUP_CANCEL_OFFER, true);
LibACL._updateRoleGroup(LC.ROLE_ENTITY_CP, LC.GROUP_EXECUTE_LIMIT_OFFER, true);
@@ -62,7 +63,7 @@ library LibInitDiamond {
// setup stakeholder groups
LibACL._updateRoleGroup(LC.ROLE_UNDERWRITER, LC.GROUP_UNDERWRITERS, true);
LibACL._updateRoleGroup(LC.ROLE_BROKER, LC.GROUP_BROKERS, true);
- LibACL._updateRoleGroup(LC.ROLE_CAPITAL_PROVIDER, LC.GROUP_CAPITAL_PROVIDERS, true);
+ LibACL._updateRoleGroup(LC.ROLE_ENTITY_CP, LC.GROUP_CAPITAL_PROVIDERS, true);
LibACL._updateRoleGroup(LC.ROLE_INSURED_PARTY, LC.GROUP_INSURED_PARTIES, true);
// setup assigners
diff --git a/src/libs/LibMarket.sol b/src/libs/LibMarket.sol
index 1657e484..88c42268 100644
--- a/src/libs/LibMarket.sol
+++ b/src/libs/LibMarket.sol
@@ -230,10 +230,8 @@ library LibMarket {
if (_buyAmount * calcs.currentSellAmount < calcs.currentBuyAmount * _sellAmount) {
if (buyExternalToken) {
// Normalize the sell amount when taker is buying external tokens
- calcs.normalizedBuyAmount = (_buyAmount * calcs.currentSellAmount) / _sellAmount;
- calcs.normalizedSellAmount = calcs.currentSellAmount;
- // if the taker is buying an external token, we need to normalize current buy amount value
+ // if the taker is buying an external token, we need to normalize current buy amount value
// normalization factor = taker price / maker price:
// = (initial buy amount/initial sell amount) / (current buy amount / current sell amount)
// = initial buy amount * current sell amount / initial sell amount / current buy amount
@@ -241,30 +239,26 @@ library LibMarket {
// = current buy amount * normalization factor
// normalized buy amount = current buy amount * (initial buy amount * current sell amount / initial sell amount / current buy amount)
// which equals to below:
- result.remainingBuyAmount -= (_buyAmount * calcs.currentSellAmount) / _sellAmount;
- result.remainingSellAmount -= calcs.currentSellAmount;
+ calcs.normalizedBuyAmount = (_buyAmount * calcs.currentSellAmount) / _sellAmount;
+ calcs.normalizedSellAmount = calcs.currentSellAmount;
} else {
// Normalize the sell amount when taker is buying participation tokens
- calcs.normalizedSellAmount = (_sellAmount * calcs.currentBuyAmount) / _buyAmount;
- calcs.normalizedBuyAmount = calcs.currentBuyAmount;
// if the taker is buying participation tokens we need to normalize current sell amount value
- result.remainingBuyAmount -= calcs.currentBuyAmount;
- result.remainingSellAmount -= (_sellAmount * calcs.currentBuyAmount) / _buyAmount;
+ calcs.normalizedBuyAmount = calcs.currentBuyAmount;
+ calcs.normalizedSellAmount = (_sellAmount * calcs.currentBuyAmount) / _buyAmount;
}
-
- emit OrderMatched(_offerId, bestOfferId, calcs.normalizedSellAmount, calcs.normalizedBuyAmount); // taker offer
- emit OrderMatched(bestOfferId, _offerId, calcs.normalizedBuyAmount, calcs.normalizedSellAmount); // maker offer
+ result.remainingBuyAmount -= calcs.normalizedBuyAmount;
+ result.remainingSellAmount -= calcs.normalizedSellAmount;
} else {
result.remainingBuyAmount -= calcs.currentBuyAmount;
result.remainingSellAmount -= calcs.currentSellAmount;
-
- emit OrderMatched(_offerId, bestOfferId, calcs.currentSellAmount, calcs.currentBuyAmount); // taker offer
- emit OrderMatched(bestOfferId, _offerId, calcs.currentBuyAmount, calcs.currentSellAmount); // maker offer
}
// note: events are emmited to keep track of average price actually paid,
// in case matched is done with more preferable offers, otherwise this information is be lost
+ emit OrderMatched(_offerId, bestOfferId, calcs.currentSellAmount, calcs.currentBuyAmount); // taker offer
+ emit OrderMatched(bestOfferId, _offerId, calcs.currentBuyAmount, calcs.currentSellAmount); // maker offer
}
}
diff --git a/src/libs/LibSimplePolicy.sol b/src/libs/LibSimplePolicy.sol
index 6fc817f1..d4ef2638 100644
--- a/src/libs/LibSimplePolicy.sol
+++ b/src/libs/LibSimplePolicy.sol
@@ -10,12 +10,10 @@ import { LibTokenizedVault } from "./LibTokenizedVault.sol";
import { LibFeeRouter } from "./LibFeeRouter.sol";
import { LibHelpers } from "./LibHelpers.sol";
import { LibEIP712 } from "./LibEIP712.sol";
-import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
-import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
-import { ExcessiveCommissionReceivers, FeeBasisPointsExceedHalfMax, EntityDoesNotExist, PolicyDoesNotExist, PolicyCannotCancelAfterMaturation, PolicyIdCannotBeZero, DuplicateSignerCreatingSimplePolicy, SimplePolicyStakeholderSignatureInvalid, SimplePolicyClaimsPaidShouldStartAtZero, SimplePolicyPremiumsPaidShouldStartAtZero, CancelCannotBeTrueWhenCreatingSimplePolicy, InvalidSignatureError, InvalidSignatureSError, MaturationDateTooFar } from "../shared/CustomErrors.sol";
+
+import { ExcessiveCommissionReceivers, FeeBasisPointsExceedHalfMax, EntityDoesNotExist, PolicyDoesNotExist, PolicyCannotCancelAfterMaturation, PolicyIdCannotBeZero, DuplicateSignerCreatingSimplePolicy, SimplePolicyStakeholderSignatureInvalid, SimplePolicyClaimsPaidShouldStartAtZero, SimplePolicyPremiumsPaidShouldStartAtZero, CancelCannotBeTrueWhenCreatingSimplePolicy, MaturationDateTooFar } from "../shared/CustomErrors.sol";
library LibSimplePolicy {
- using ECDSA for bytes32;
/**
* @notice New policy has been created
* @dev Emitted when policy is created
@@ -68,7 +66,7 @@ library LibSimplePolicy {
for (uint256 i = 0; i < rolesCount; i++) {
previousSigner = signer;
- signer = getSigner(signingHash, _stakeholders.signatures[i]);
+ signer = LibAdmin._getSigner(signingHash, _stakeholders.signatures[i]);
if (LibObject._getParentFromAddress(signer) != _stakeholders.entityIds[i]) {
revert SimplePolicyStakeholderSignatureInvalid(
@@ -92,39 +90,6 @@ library LibSimplePolicy {
emit SimplePolicyCreated(_policyId, _entityId);
}
- function getSigner(bytes32 signingHash, bytes memory signature) private pure returns (address) {
- bytes32 r;
- bytes32 s;
- uint8 v;
-
- // ecrecover takes the signature parameters, and the only way to get them
- if (signature.length == 65) {
- // currently is to use assembly.
- /// @solidity memory-safe-assembly
- assembly {
- r := mload(add(signature, 0x20))
- s := mload(add(signature, 0x40))
- v := byte(0, mload(add(signature, 0x60)))
-
- switch v
- // if v == 0, then v = 27
- case 0 {
- v := 27
- }
- // if v == 1, then v = 28
- case 1 {
- v := 28
- }
- }
- }
-
- (address signer, ECDSA.RecoverError err, ) = ECDSA.tryRecover(MessageHashUtils.toEthSignedMessageHash(signingHash), v, r, s);
-
- if (err == ECDSA.RecoverError.InvalidSignature) revert InvalidSignatureError(signingHash);
- else if (err == ECDSA.RecoverError.InvalidSignatureS) revert InvalidSignatureSError(s);
-
- return signer;
- }
/**
* @dev If an entity passes their checks to create a policy, ensure that the entity's capacity is appropriately decreased by the amount of capital that will be tied to the new policy being created.
*/
diff --git a/src/libs/LibTokenizedVault.sol b/src/libs/LibTokenizedVault.sol
index 9462129a..1b0ab30f 100644
--- a/src/libs/LibTokenizedVault.sol
+++ b/src/libs/LibTokenizedVault.sol
@@ -7,7 +7,7 @@ import { LibConstants as LC } from "./LibConstants.sol";
import { LibHelpers } from "./LibHelpers.sol";
import { LibObject } from "./LibObject.sol";
import { LibERC20 } from "./LibERC20.sol";
-import { RebasingInterestNotInitialized, RebasingInterestInsufficient, RebasingAmountInvalid, RebasingSupplyDecreased } from "../shared/CustomErrors.sol";
+import { RebasingInterestNotInitialized, RebasingInterestInsufficient, RebasingSupplyDecreased } from "../shared/CustomErrors.sol";
import { InsufficientBalance } from "../shared/CustomErrors.sol";
@@ -189,7 +189,7 @@ library LibTokenizedVault {
if (withdrawableDividend > 0) {
// Bump the withdrawn dividends for the owner
/// Special Case: (_tokenId == _dividendTokenId), i.e distributing accrued interest for rebasing coins like USDM
- /// withdrawnDividendPerOwner should be adjusted before tha update, so that the user cannot claim additional dividend based on the amount he just received as dividend
+ /// withdrawnDividendPerOwner should be adjusted before the update, so that the user cannot claim additional dividend based on the amount he just received as dividend
/// dividend is calculated based on a ratio between users balance and the total, but in this case claiming the dividend his balance increases and
/// thus his share of the total increases as well, which entitles him to claim more of the dividend, potentially draining out the entirety of it if repeated infinitely
if (_tokenId == _dividendTokenId) {
@@ -297,6 +297,13 @@ library LibTokenizedVault {
return s.lockedBalances[_accountId][_tokenId];
}
+ function _getAvailableBalance(bytes32 _accountId, bytes32 _tokenId) internal view returns (uint256 amount) {
+ AppStorage storage s = LibAppStorage.diamondStorage();
+ uint256 lockedBalance = s.lockedBalances[_accountId][_tokenId];
+ uint256 internalBalance = s.tokenBalances[_tokenId][_accountId];
+ return internalBalance - lockedBalance;
+ }
+
function _totalDividends(bytes32 _tokenId, bytes32 _dividendDenominationId) internal view returns (uint256) {
AppStorage storage s = LibAppStorage.diamondStorage();
diff --git a/src/libs/LibTokenizedVaultStaking.sol b/src/libs/LibTokenizedVaultStaking.sol
index 6fb3d607..352baaa2 100644
--- a/src/libs/LibTokenizedVaultStaking.sol
+++ b/src/libs/LibTokenizedVaultStaking.sol
@@ -317,6 +317,28 @@ library LibTokenizedVaultStaking {
s.stakeBalance[vTokenId][_stakerId] = state.balance;
}
+ function _compoundRewards(bytes32 _stakerId, bytes32 _entityId, uint64 _interval) internal {
+ AppStorage storage s = LibAppStorage.diamondStorage();
+ bytes32 tokenId = s.stakingConfigs[_entityId].tokenId;
+
+ (, RewardsBalances memory rewards) = _getStakingStateWithRewardsBalances(_stakerId, _entityId, _interval);
+
+ uint256 rewardAmount;
+ uint256 rewardCount = rewards.currencies.length;
+
+ for (uint64 i = 0; i < rewardCount; i++) {
+ if (rewards.currencies[i] == tokenId) {
+ rewardAmount = rewards.amounts[i];
+ break;
+ }
+ }
+
+ require(rewardAmount > 0, "No reward to compound");
+
+ _collectRewards(_stakerId, _entityId, _interval);
+ _stake(_stakerId, _entityId, rewardAmount);
+ }
+
function _collectRewards(bytes32 _stakerId, bytes32 _entityId, uint64 _interval) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
diff --git a/src/shared/CustomErrors.sol b/src/shared/CustomErrors.sol
index ad8de610..ff2471ff 100644
--- a/src/shared/CustomErrors.sol
+++ b/src/shared/CustomErrors.sol
@@ -29,15 +29,9 @@ error InvalidGroupPrivilege(bytes32 msgSenderId, bytes32 context, string roleInC
/// @param role The name of the rle which should not be approaved for self-onboarding
error InvalidSelfOnboardRoleApproval(bytes32 role);
-/// @dev Passing in a missing address when trying to add a token address to the supported external token list.
-error CannotAddNullSupportedExternalToken();
-
/// @dev Cannot add a ERC20 token to the supported external token list that has more than 18 decimal places.
error CannotSupportExternalTokenWithMoreThan18Decimals();
-/// @dev Passing in a missing address when trying to assign a new token address as the new discount token.
-error CannotAddNullDiscountToken();
-
/// @dev Object exsists when it should not.
error ObjectExistsAlready(bytes32 objectId);
@@ -50,9 +44,6 @@ error EntityDoesNotExist(bytes32 objectId);
/// @dev The entity self onboarding not approved
error EntityOnboardingNotApproved(address userAddress);
-/// @dev The entity self onboarding already approved
-error EntityOnboardingAlreadyApproved(address userAddress);
-
/// @dev Cannot create an entity that already exists.
error EntityExistsAlready(bytes32 entityId);
@@ -129,9 +120,6 @@ error RebasingInterestNotInitialized(bytes32 tokenId);
/// @dev Insufficient amount of interest acrrued so far
error RebasingInterestInsufficient(bytes32 tokenId, uint256 amount, uint256 accruedAmount);
-/// @dev Rebase amount cannot be greater than the actual balance
-error RebasingAmountInvalid(bytes32 tokenId, uint256 amount, uint256 currentBalance);
-
/// @dev Staking can be initialized only once
error StakingAlreadyStarted(bytes32 entityId, bytes32 tokenId);
@@ -179,6 +167,10 @@ error RebasingSupplyDecreased(bytes32 tokenId, uint256 accountInNayms, uint256 a
/// @notice This error suggests that the signature itself is malformed or does not correspond to the hash provided.
error InvalidSignatureError(bytes32 hash);
+/// @dev This error indicates that the signatures is not 65 bytes.
+/// @notice This error indicates invalid signature length.
+error InvalidSignatureLength();
+
/// @dev This error is used to indicate that the signature has an invalid 's' value.
/// @param sValue The 's' value of the ECDSA signature that was deemed invalid.
/// @notice This error is triggered when the 's' value of the signature is not within the lower half of the secp256k1 curve's order, which can lead to malleability issues.
@@ -187,9 +179,6 @@ error InvalidSignatureSError(bytes32 sValue);
/// @dev Thrown when the number of receivers specified in a transaction is not within the acceptable range.
error InvalidReceiverCount(uint256 numberOfReceivers);
-/// @dev The entity ID is invalid for the given context.
-error InvalidEntityId(bytes32 entityId);
-
/// @dev Thrown when the maturation date of a policy is set beyond the allowable future date limit.
/// This prevents setting unrealistic maturation dates that could affect the contract's operability or the enforceability of the policy.
error MaturationDateTooFar(uint256 maturationDate);
@@ -212,3 +201,8 @@ error InvalidTokenId();
/// @dev Cannot stake an amount lower than objectMinimumSell[tokenId].
error InvalidStakingAmount();
+
+/// @dev Not a supported external ERC 20 token
+/// @param tokenAddress Address of the ERC20 token
+/// @param method Name of the method that threw the error
+error InvalidERC20Token(address tokenAddress, string method);
diff --git a/src/shared/FreeStructs.sol b/src/shared/FreeStructs.sol
index a10206ca..68f419d3 100644
--- a/src/shared/FreeStructs.sol
+++ b/src/shared/FreeStructs.sol
@@ -40,6 +40,7 @@ struct Entity {
bool simplePolicyEnabled;
}
+// DEPRECATED, but don't remove, referenced in appstorage
struct EntityApproval {
bytes32 entityId;
bytes32 roleId;
@@ -126,3 +127,16 @@ struct RewardsBalances {
uint256[] amounts;
uint64 lastPaidInterval;
}
+
+struct PermitSignature {
+ uint256 deadline;
+ uint8 v;
+ bytes32 r;
+ bytes32 s;
+}
+
+struct OnboardingApproval {
+ bytes32 entityId;
+ bytes32 roleId;
+ bytes signature;
+}
diff --git a/src/shared/Modifiers.sol b/src/shared/Modifiers.sol
index aabe2f91..94a6d6ed 100644
--- a/src/shared/Modifiers.sol
+++ b/src/shared/Modifiers.sol
@@ -4,12 +4,8 @@ pragma solidity 0.8.20;
/// @notice modifiers
import { LibAdmin } from "../libs/LibAdmin.sol";
-import { LibConstants as LC } from "../libs/LibConstants.sol";
-import { LibHelpers } from "../libs/LibHelpers.sol";
-import { LibObject } from "../libs/LibObject.sol";
import { LibACL } from "../libs/LibACL.sol";
-import { InvalidGroupPrivilege } from "./CustomErrors.sol";
-import { LibString } from "solady/utils/LibString.sol";
+import { LibObject } from "../libs/LibObject.sol";
/**
* @title Modifiers
@@ -17,25 +13,13 @@ import { LibString } from "solady/utils/LibString.sol";
* @dev Function modifiers to control access
*/
contract Modifiers {
- using LibHelpers for *;
- using LibACL for *;
- using LibString for *;
-
modifier notLocked() {
require(!LibAdmin._isFunctionLocked(msg.sig), "function is locked");
_;
}
modifier assertPrivilege(bytes32 _context, string memory _group) {
- if (!msg.sender._getIdForAddress()._hasGroupPrivilege(_context, _group._stringToBytes32()))
- /// Note: If the role returned by `_getRoleInContext` is empty (represented by bytes32(0)), we explicitly return an empty string.
- /// This ensures the user doesn't receive a string that could potentially include unwanted data (like pointer and length) without any meaningful content.
- revert InvalidGroupPrivilege(
- msg.sender._getIdForAddress(),
- _context,
- (msg.sender._getIdForAddress()._getRoleInContext(_context) == bytes32(0)) ? "" : msg.sender._getIdForAddress()._getRoleInContext(_context).fromSmallString(),
- _group
- );
+ LibACL._assertPriviledge(_context, _group);
_;
}
diff --git a/test/T01Deployment.t.sol b/test/T01Deployment.t.sol
index 1bcb11f3..bb2e5284 100644
--- a/test/T01Deployment.t.sol
+++ b/test/T01Deployment.t.sol
@@ -8,7 +8,6 @@ import { D03ProtocolDefaults } from "./defaults/D03ProtocolDefaults.sol";
import { InitDiamondFixture } from "./fixtures/InitDiamondFixture.sol";
import { IDiamondLoupe } from "lib/diamond-2-hardhat/contracts/interfaces/IDiamondLoupe.sol";
import { IDiamondCut } from "lib/diamond-2-hardhat/contracts/interfaces/IDiamondCut.sol";
-import { IDiamondProxy } from "src/generated/IDiamondProxy.sol";
import { DiamondAlreadyInitialized } from "src/init/InitDiamond.sol";
import { LibGovernance } from "src/libs/LibGovernance.sol";
diff --git a/test/T01LibERC20.t.sol b/test/T01LibERC20.t.sol
index b9efd51e..fe92f56b 100644
--- a/test/T01LibERC20.t.sol
+++ b/test/T01LibERC20.t.sol
@@ -2,7 +2,6 @@
pragma solidity 0.8.20;
// solhint-disable no-global-import
-import { Vm } from "forge-std/Vm.sol";
import { D03ProtocolDefaults } from "./defaults/D03ProtocolDefaults.sol";
import { DummyToken } from "./utils/DummyToken.sol";
import { BadToken } from "./utils/BadToken.sol";
@@ -81,10 +80,6 @@ contract T01LibERC20 is D03ProtocolDefaults {
vm.expectRevert("not enough balance");
fixture.transfer(tokenAddress, account0, 101);
- // failed transfer of 0
- vm.expectRevert("LibERC20: transfer or transferFrom returned false");
- fixture.transfer(tokenAddress, account0, 0);
-
// successful transfer
fixture.transfer(tokenAddress, account0, 100);
@@ -113,10 +108,6 @@ contract T01LibERC20 is D03ProtocolDefaults {
vm.expectRevert("not enough balance");
fixture.transferFrom(tokenAddress, signer1, account0, 101);
- // failed transfer of 0 reverts with empty string
- vm.expectRevert("LibERC20: transfer or transferFrom reverted");
- fixture.transferFrom(tokenAddress, signer1, account0, 0);
-
// successful transfer
fixture.transferFrom(tokenAddress, signer1, account0, 100);
diff --git a/test/T01LibHelpers.t.sol b/test/T01LibHelpers.t.sol
index 26fbf15e..ae71680b 100644
--- a/test/T01LibHelpers.t.sol
+++ b/test/T01LibHelpers.t.sol
@@ -2,7 +2,6 @@
pragma solidity 0.8.20;
import { Test } from "forge-std/Test.sol";
-import { Vm } from "forge-std/Vm.sol";
import { LibHelpers } from "../src/libs/LibHelpers.sol";
contract T01LibHelpers is Test {
diff --git a/test/T02ACL.t.sol b/test/T02ACL.t.sol
index 733369a3..2e71e8f6 100644
--- a/test/T02ACL.t.sol
+++ b/test/T02ACL.t.sol
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
-import { console2 } from "forge-std/console2.sol";
import { D03ProtocolDefaults, LibHelpers, LC } from "./defaults/D03ProtocolDefaults.sol";
import { MockAccounts } from "test/utils/users/MockAccounts.sol";
import { Vm } from "forge-std/Vm.sol";
@@ -92,26 +91,11 @@ contract T02ACLTest is D03ProtocolDefaults, MockAccounts {
string memory role = LC.ROLE_ENTITY_ADMIN;
vm.recordLogs();
-
nayms.assignRole(signer1Id, context, role);
Vm.Log[] memory entries = vm.getRecordedLogs();
-
- assertEq(entries[0].topics.length, 2);
- assertEq(entries[0].topics[0], keccak256("RoleUpdated(bytes32,bytes32,bytes32,string)"));
- assertEq(entries[0].topics[1], signer1Id);
- (bytes32 contextId, bytes32 roleId, string memory action) = abi.decode(entries[0].data, (bytes32, bytes32, string));
- assertEq(contextId, context);
- assertEq(roleId, bytes32(0));
- assertEq(action, "_unassignRole");
-
- assertEq(entries[1].topics.length, 2);
- assertEq(entries[1].topics[0], keccak256("RoleUpdated(bytes32,bytes32,bytes32,string)"));
- assertEq(entries[1].topics[1], signer1Id);
- (contextId, roleId, action) = abi.decode(entries[1].data, (bytes32, bytes32, string));
- assertEq(contextId, context);
- assertEq(roleId, LibHelpers._stringToBytes32(role));
- assertEq(action, "_assignRole");
+ assertRoleUpdateEvent(entries, 0, context, signer1Id, bytes32(0), "_unassignRole");
+ assertRoleUpdateEvent(entries, 1, context, signer1Id, LibHelpers._stringToBytes32(role), "_assignRole");
}
function testInvalidObjectIdWhenAssignRole() public {
diff --git a/test/T02Access.t.sol b/test/T02Access.t.sol
index e46fb6ba..89864d29 100644
--- a/test/T02Access.t.sol
+++ b/test/T02Access.t.sol
@@ -1,14 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
-// import { c as c } from "forge-std/c.sol";
+
import { D03ProtocolDefaults, LibHelpers, LC, c } from "./defaults/D03ProtocolDefaults.sol";
import { Entity, SimplePolicy, Stakeholders } from "src/shared/FreeStructs.sol";
import "src/shared/CustomErrors.sol";
-// updateRoleGroup | isRoleInGroup | groups [role][group] = bool
-// updateRoleAssigner | canGroupAssignRole | canAssign [role] = group
-// getRoleInContext | roles[objectId][contextId] = role
-
contract T02Access is D03ProtocolDefaults {
using LibHelpers for *;
diff --git a/test/T02Admin.t.sol b/test/T02Admin.t.sol
index 1d0c969e..2ffff6df 100644
--- a/test/T02Admin.t.sol
+++ b/test/T02Admin.t.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.20;
import { D03ProtocolDefaults, LibHelpers, LC } from "./defaults/D03ProtocolDefaults.sol";
-import { Entity, Stakeholders, SimplePolicy } from "../src/shared/FreeStructs.sol";
+import { Entity, Stakeholders, SimplePolicy, PermitSignature, OnboardingApproval } from "../src/shared/FreeStructs.sol";
import { MockAccounts } from "test/utils/users/MockAccounts.sol";
import { Vm } from "forge-std/Vm.sol";
import { IDiamondProxy } from "../src/generated/IDiamondProxy.sol";
@@ -241,7 +241,7 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts {
assertEq(entries[0].topics[0], keccak256("FunctionsLocked(bytes4[])"));
(s_functionSelectors) = abi.decode(entries[0].data, (bytes4[]));
- bytes4[] memory lockedFunctions = new bytes4[](22);
+ bytes4[] memory lockedFunctions = new bytes4[](25);
lockedFunctions[0] = IDiamondProxy.startTokenSale.selector;
lockedFunctions[1] = IDiamondProxy.paySimpleClaim.selector;
lockedFunctions[2] = IDiamondProxy.paySimplePremium.selector;
@@ -264,6 +264,9 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts {
lockedFunctions[19] = IDiamondProxy.createSimplePolicy.selector;
lockedFunctions[20] = IDiamondProxy.createEntity.selector;
lockedFunctions[21] = IDiamondProxy.collectRewardsToInterval.selector;
+ lockedFunctions[22] = IDiamondProxy.compoundRewards.selector;
+ lockedFunctions[23] = IDiamondProxy.zapStake.selector;
+ lockedFunctions[24] = IDiamondProxy.zapOrder.selector;
for (uint256 i = 0; i < lockedFunctions.length; i++) {
assertTrue(nayms.isFunctionLocked(lockedFunctions[i]));
@@ -323,6 +326,18 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts {
vm.expectRevert("function is locked");
nayms.collectRewards(bytes32(0));
+ vm.expectRevert("function is locked");
+ nayms.compoundRewards(bytes32(0));
+
+ PermitSignature memory permSig;
+ OnboardingApproval memory onboardingApproval;
+
+ vm.expectRevert("function is locked");
+ nayms.zapStake(address(0), bytes32(0), 0, 0, permSig, onboardingApproval);
+
+ vm.expectRevert("function is locked");
+ nayms.zapOrder(address(0), 0, bytes32(0), 0, bytes32(0), 0, permSig, onboardingApproval);
+
vm.expectRevert("function is locked");
nayms.collectRewardsToInterval(bytes32(0), 5);
@@ -359,8 +374,11 @@ contract T02AdminTest is D03ProtocolDefaults, MockAccounts {
assertFalse(nayms.isFunctionLocked(IDiamondProxy.payReward.selector), "function payReward locked");
assertFalse(nayms.isFunctionLocked(IDiamondProxy.collectRewards.selector), "function collectRewards locked");
assertFalse(nayms.isFunctionLocked(IDiamondProxy.collectRewardsToInterval.selector), "function collectRewardsToInterval locked");
+ assertFalse(nayms.isFunctionLocked(IDiamondProxy.compoundRewards.selector), "function compoundRewards locked");
assertFalse(nayms.isFunctionLocked(IDiamondProxy.cancelSimplePolicy.selector), "function cancelSimplePolicy locked");
assertFalse(nayms.isFunctionLocked(IDiamondProxy.createSimplePolicy.selector), "function createSimplePolicy locked");
assertFalse(nayms.isFunctionLocked(IDiamondProxy.createEntity.selector), "function createEntity locked");
+ assertFalse(nayms.isFunctionLocked(IDiamondProxy.zapStake.selector), "function zapStake locked");
+ assertFalse(nayms.isFunctionLocked(IDiamondProxy.zapOrder.selector), "function zapOrder locked");
}
}
diff --git a/test/T02User.t.sol b/test/T02User.t.sol
index a5e387ff..a02fa3d8 100644
--- a/test/T02User.t.sol
+++ b/test/T02User.t.sol
@@ -3,7 +3,6 @@ pragma solidity 0.8.20;
import { D03ProtocolDefaults, LibHelpers, LC } from "./defaults/D03ProtocolDefaults.sol";
import { MockAccounts } from "test/utils/users/MockAccounts.sol";
-import { Vm } from "forge-std/Vm.sol";
import "../src/shared/CustomErrors.sol";
contract T02UserTest is D03ProtocolDefaults, MockAccounts {
diff --git a/test/T03NaymsOwnership.t.sol b/test/T03NaymsOwnership.t.sol
index fae797be..803742a4 100644
--- a/test/T03NaymsOwnership.t.sol
+++ b/test/T03NaymsOwnership.t.sol
@@ -48,10 +48,10 @@ contract T03NaymsOwnershipTest is D03ProtocolDefaults, MockAccounts {
function testFuzz_TransferOwnership(address newOwner, address notSysAdmin, address anotherSysAdmin) public {
vm.assume(newOwner != anotherSysAdmin && newOwner != account0 && newOwner != address(0) && anotherSysAdmin != address(0));
vm.assume(anotherSysAdmin != address(0));
+ vm.assume(!nayms.isInGroup(newOwner._getIdForAddress(), systemContext, LC.GROUP_SYSTEM_MANAGERS));
- bytes32 notSysAdminId = LibHelpers._getIdForAddress(address(notSysAdmin));
// note: for this test, assume that the notSysAdmin address is not a system admin
- vm.assume(!nayms.isInGroup(notSysAdminId, systemContext, LC.GROUP_SYSTEM_ADMINS));
+ vm.assume(!nayms.isInGroup(notSysAdmin._getIdForAddress(), systemContext, LC.GROUP_SYSTEM_ADMINS));
vm.label(newOwner, "newOwner");
vm.label(notSysAdmin, "notSysAdmin");
diff --git a/test/T03SystemFacet.t.sol b/test/T03SystemFacet.t.sol
index 39351c93..a7772f6c 100644
--- a/test/T03SystemFacet.t.sol
+++ b/test/T03SystemFacet.t.sol
@@ -2,10 +2,7 @@
pragma solidity 0.8.20;
import { D03ProtocolDefaults, LibHelpers, LibAdmin, LC, c } from "./defaults/D03ProtocolDefaults.sol";
-
import { MockAccounts } from "./utils/users/MockAccounts.sol";
-
-import { Entity } from "../src/shared/AppStorage.sol";
import "../src/shared/CustomErrors.sol";
contract T03SystemFacetTest is D03ProtocolDefaults, MockAccounts {
diff --git a/test/T03TokenizedVault.t.sol b/test/T03TokenizedVault.t.sol
index 38df1bbf..23b821ca 100644
--- a/test/T03TokenizedVault.t.sol
+++ b/test/T03TokenizedVault.t.sol
@@ -3,7 +3,7 @@ pragma solidity 0.8.20;
import { MockAccounts } from "./utils/users/MockAccounts.sol";
import { c, D03ProtocolDefaults, LibHelpers, LC } from "./defaults/D03ProtocolDefaults.sol";
-import { Entity, CalculatedFees } from "../src/shared/AppStorage.sol";
+import { Entity } from "../src/shared/AppStorage.sol";
import { IDiamondCut } from "lib/diamond-2-hardhat/contracts/interfaces/IDiamondCut.sol";
import { TokenizedVaultFixture } from "test/fixtures/TokenizedVaultFixture.sol";
import "src/shared/CustomErrors.sol";
@@ -74,7 +74,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts {
require(success, "Should get commissions from app storage");
}
- function testGetLockedBalance() public {
+ function testGetLockedAndAvailableBalance() public {
changePrank(sm.addr);
bytes32 entityId = createTestEntity(account0Id);
@@ -85,7 +85,10 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts {
nayms.enableEntityTokenization(entityId, "Entity1", "Entity1 Token", 1);
nayms.startTokenSale(entityId, 100, 100);
- assertEq(nayms.getLockedBalance(entityId, entityId), 100);
+ uint256 internalBalance = nayms.internalBalanceOf(entityId, entityId);
+ uint256 lockedBalance = nayms.getLockedBalance(entityId, entityId);
+ assertEq(lockedBalance, 100, "invalid locked balance");
+ assertEq(nayms.getAvailableBalance(entityId, entityId), internalBalance - lockedBalance, "invalid avaiable balance");
}
function testSingleExternalDeposit() public {
@@ -106,7 +109,7 @@ contract T03TokenizedVaultTest is D03ProtocolDefaults, MockAccounts {
vm.expectRevert("extDeposit: invalid receiver");
nayms.externalDeposit(wethAddress, 1);
- vm.expectRevert("extDeposit: invalid ERC20 token");
+ vm.expectRevert(abi.encodeWithSelector(InvalidERC20Token.selector, address(0xBADAAAAAAAAA), "extDeposit"));
nayms.externalDeposit(address(0xBADAAAAAAAAA), 1);
// deposit to entity1
diff --git a/test/T04Entity.t.sol b/test/T04Entity.t.sol
index 56a16f57..067433a2 100644
--- a/test/T04Entity.t.sol
+++ b/test/T04Entity.t.sol
@@ -4,7 +4,7 @@ pragma solidity 0.8.20;
import { Vm } from "forge-std/Vm.sol";
import { c, D03ProtocolDefaults, LibHelpers, LC } from "./defaults/D03ProtocolDefaults.sol";
-import { Entity, MarketInfo, SimplePolicy, SimplePolicyInfo, Stakeholders } from "src/shared/FreeStructs.sol";
+import { Entity, MarketInfo, SimplePolicy, SimplePolicyInfo, Stakeholders, OnboardingApproval } from "src/shared/FreeStructs.sol";
import { IDiamondCut } from "lib/diamond-2-hardhat/contracts/interfaces/IDiamondCut.sol";
import { StdStyle } from "forge-std/StdStyle.sol";
@@ -14,9 +14,9 @@ import { SimplePolicyFixture } from "test/fixtures/SimplePolicyFixture.sol";
// solhint-disable no-global-import
import "../src/shared/CustomErrors.sol";
+// solhint-disable no-console
import { StdStyle } from "forge-std/StdStyle.sol";
-// solhint-disable no-console
contract T04EntityTest is D03ProtocolDefaults {
using LibHelpers for *;
using StdStyle for *;
@@ -339,9 +339,9 @@ contract T04EntityTest is D03ProtocolDefaults {
bytes32 signingHash = nayms.getSigningHash(simplePolicy.startDate, simplePolicy.maturationDate, simplePolicy.asset, simplePolicy.limit, testPolicyDataHash);
bytes[] memory signatures = new bytes[](3);
- signatures[0] = initPolicySig(0xACC1, signingHash); // 0x2337f702bc9A7D1f415050365634FEbEdf4054Be
- signatures[1] = initPolicySig(0xACC2, signingHash); // 0x167D6b35e51df22f42c4F42f26d365756D244fDE
- signatures[2] = initPolicySig(0xACC3, signingHash); // 0x167D6b35e51df22f42c4F42f26d365756D244fDE
+ signatures[0] = signWithPK(0xACC1, signingHash); // 0x2337f702bc9A7D1f415050365634FEbEdf4054Be
+ signatures[1] = signWithPK(0xACC2, signingHash); // 0x167D6b35e51df22f42c4F42f26d365756D244fDE
+ signatures[2] = signWithPK(0xACC3, signingHash); // 0x167D6b35e51df22f42c4F42f26d365756D244fDE
bytes32[] memory roles = new bytes32[](3);
roles[0] = LibHelpers._stringToBytes32(LC.ROLE_UNDERWRITER);
@@ -389,9 +389,9 @@ contract T04EntityTest is D03ProtocolDefaults {
bytes32 signingHash = nayms.getSigningHash(simplePolicy.startDate, simplePolicy.maturationDate, simplePolicy.asset, simplePolicy.limit, testPolicyDataHash);
bytes[] memory signatures = new bytes[](3);
- signatures[0] = initPolicySig(0xACC2, signingHash);
- signatures[1] = initPolicySig(0xACC1, signingHash);
- signatures[2] = initPolicySig(0xACC3, signingHash);
+ signatures[0] = signWithPK(0xACC2, signingHash);
+ signatures[1] = signWithPK(0xACC1, signingHash);
+ signatures[2] = signWithPK(0xACC3, signingHash);
bytes32[] memory roles = new bytes32[](3);
roles[0] = LibHelpers._stringToBytes32(LC.ROLE_UNDERWRITER);
@@ -438,10 +438,10 @@ contract T04EntityTest is D03ProtocolDefaults {
bytes32 signingHash = nayms.getSigningHash(simplePolicy.startDate, simplePolicy.maturationDate, simplePolicy.asset, simplePolicy.limit, testPolicyDataHash);
- stakeholders.signatures[0] = initPolicySig(0xACC2, signingHash);
- stakeholders.signatures[1] = initPolicySig(0xACC1, signingHash);
- stakeholders.signatures[2] = initPolicySig(0xACC3, signingHash);
- stakeholders.signatures[3] = initPolicySig(0xACC4, signingHash);
+ stakeholders.signatures[0] = signWithPK(0xACC2, signingHash);
+ stakeholders.signatures[1] = signWithPK(0xACC1, signingHash);
+ stakeholders.signatures[2] = signWithPK(0xACC3, signingHash);
+ stakeholders.signatures[3] = signWithPK(0xACC4, signingHash);
// external token not supported
vm.expectRevert("external token is not supported");
@@ -1165,139 +1165,106 @@ contract T04EntityTest is D03ProtocolDefaults {
}
function testSelfOnboardingNotApproved() public {
- vm.stopPrank();
- vm.startPrank(signer1);
- vm.expectRevert(abi.encodeWithSelector(EntityOnboardingNotApproved.selector, signer1));
- nayms.onboard();
- vm.stopPrank();
- }
-
- function testSelfOnboardingInvalidGroup() public {
- bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_SYSTEM_MANAGER);
+ bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
- vm.startPrank(em.addr);
- vm.expectRevert(abi.encodeWithSelector(InvalidSelfOnboardRoleApproval.selector, roleId));
- nayms.approveSelfOnboarding(address(111), randomEntityId(1), roleId);
- vm.stopPrank();
- }
+ bytes32 entityId = randomEntityId(1);
+ address userAddress = address(111);
+ bytes memory noSig;
- function testSelfOnboardingAlreadyApproved() public {
- nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
+ vm.startPrank(userAddress);
+ vm.expectRevert(abi.encodeWithSelector(EntityOnboardingNotApproved.selector, userAddress));
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: entityId, roleId: roleId, signature: noSig }));
- bytes32 entityId = randomEntityId(2);
- bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
- vm.startPrank(em.addr);
- nayms.approveSelfOnboarding(address(111), entityId, roleId);
+ bytes memory sig = signWithPK(em.pk, nayms.getOnboardingHash(userAddress, entityId, roleId));
+
+ vm.expectRevert(abi.encodeWithSelector(EntityOnboardingNotApproved.selector, userAddress));
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: 0x0, roleId: roleId, signature: sig }));
+
+ vm.expectRevert(abi.encodeWithSelector(EntityOnboardingNotApproved.selector, userAddress));
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: entityId, roleId: 0x0, signature: sig }));
+ vm.stopPrank();
- vm.expectRevert(abi.encodeWithSelector(EntityOnboardingAlreadyApproved.selector, address(111)));
- nayms.approveSelfOnboarding(address(111), entityId, roleId);
+ vm.startPrank(sm.addr);
+ nayms.assignRole(em.id, systemContext, LC.ROLE_ENTITY_CP); // remove onboarding approver role
vm.stopPrank();
+
+ vm.startPrank(userAddress);
+ vm.expectRevert(abi.encodeWithSelector(EntityOnboardingNotApproved.selector, userAddress));
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: entityId, roleId: roleId, signature: sig }));
}
- function testSelfOnboardingSuccess() public {
+ function testSelfOnboardingInvalidGroup() public {
+ bytes32 sysMgrRoleId = LibHelpers._stringToBytes32(LC.ROLE_SYSTEM_MANAGER);
nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
- bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
- bytes32 e1 = randomEntityId(1);
- _approveSelfOnboarding(address(111), e1, roleId);
- _selfOnboard(address(111), e1, LC.GROUP_TOKEN_HOLDERS);
+ bytes32 entityId = randomEntityId(1);
+ address userAddress = address(111);
- bytes32 roleId2 = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
- bytes32 e2 = randomEntityId(2);
- _approveSelfOnboarding(address(222), e2, roleId2);
- _selfOnboard(address(222), e2, LC.GROUP_CAPITAL_PROVIDERS);
+ bytes memory sig = signWithPK(em.pk, nayms.getOnboardingHash(userAddress, entityId, sysMgrRoleId));
+
+ vm.startPrank(userAddress);
+ vm.expectRevert(abi.encodeWithSelector(InvalidSelfOnboardRoleApproval.selector, sysMgrRoleId));
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: entityId, roleId: sysMgrRoleId, signature: sig }));
+ vm.stopPrank();
}
- function testSelfOnboardingUpgradeToCapitalProvider() public {
+ function testSelfOnboardingSuccess() public {
+ bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
- bytes32 roleIdTokenHolder = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
- bytes32 roleIdCP = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
- // test upgrade before onboarding
- bytes32 e1 = randomEntityId(1);
- _approveSelfOnboarding(address(111), e1, roleIdTokenHolder);
- _approveSelfOnboarding(address(111), e1, roleIdCP);
- _selfOnboard(address(111), e1, LC.GROUP_CAPITAL_PROVIDERS);
+ bytes32 entityId = randomEntityId(1);
+ address userAddress = address(111);
- // test upgrade after onboarding
- bytes32 e2 = randomEntityId(2);
- _approveSelfOnboarding(address(222), e2, roleIdTokenHolder);
- _selfOnboard(address(222), e2, LC.GROUP_TOKEN_HOLDERS);
- _approveSelfOnboarding(address(222), e2, roleIdCP);
+ bytes memory sig = signWithPK(em.pk, nayms.getOnboardingHash(userAddress, entityId, roleId));
- vm.recordLogs();
+ vm.startPrank(userAddress);
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: entityId, roleId: roleId, signature: sig }));
+ vm.stopPrank();
- _selfOnboard(address(222), e2, LC.GROUP_CAPITAL_PROVIDERS);
+ assertEq(nayms.getEntity(LibHelpers._getIdForAddress(userAddress)), entityId, "parent should be set");
- Vm.Log[] memory entries = vm.getRecordedLogs();
- assertEq(entries[0].topics.length, 2);
- assertEq(entries[0].topics[0], keccak256("RoleUpdated(bytes32,bytes32,bytes32,string)"));
- assertEq(entries[0].topics[1], e2);
- (bytes32 contextId, bytes32 roleId, string memory action) = abi.decode(entries[0].data, (bytes32, bytes32, string));
- assertEq(contextId, e2);
- assertEq(roleId, roleIdTokenHolder);
- assertEq(action, "_unassignRole");
-
- assertEq(entries[1].topics.length, 2);
- assertEq(entries[1].topics[0], keccak256("RoleUpdated(bytes32,bytes32,bytes32,string)"));
- assertEq(entries[1].topics[1], e2, "object ID doesn't match");
- (bytes32 contextId2, bytes32 roleId2, string memory action2) = abi.decode(entries[1].data, (bytes32, bytes32, string));
- assertEq(contextId2, systemContext, "incorrect context");
- assertEq(roleId2, roleIdTokenHolder, "wrong role");
- assertEq(action2, "_unassignRole", "wrong operation");
+ assertTrue(nayms.isInGroup(entityId, systemContext, LC.GROUP_CAPITAL_PROVIDERS), "should belong capital providers group");
}
- function testSelfOnboardingCancel() public {
+ function testSelfOnboardingUpgradeToCapitalProvider() public {
nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
- bytes32 entityId = randomEntityId(2);
+ address userAddress = address(111);
bytes32 roleIdTokenHolder = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
+ bytes32 roleIdCapitalProvider = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
- vm.startPrank(em.addr);
- nayms.approveSelfOnboarding(address(111), entityId, roleIdTokenHolder);
- assertTrue(nayms.isSelfOnboardingApproved(address(111), entityId, roleIdTokenHolder), "Onboarding should be approved");
+ bytes32 e1 = randomEntityId(1);
- vm.expectRevert(abi.encodeWithSelector(InvalidGroupPrivilege.selector, em.addr._getIdForAddress(), systemContext, LC.ROLE_ONBOARDING_APPROVER, LC.GROUP_SYSTEM_MANAGERS));
- nayms.cancelSelfOnboarding(address(111));
- vm.stopPrank();
- vm.startPrank(sm.addr);
- nayms.cancelSelfOnboarding(address(111));
+ bytes memory sigTokenHolder = signWithPK(em.pk, nayms.getOnboardingHash(userAddress, e1, roleIdTokenHolder));
+ bytes memory sigCapitalProvider = signWithPK(em.pk, nayms.getOnboardingHash(userAddress, e1, roleIdCapitalProvider));
- assertFalse(nayms.isSelfOnboardingApproved(address(111), entityId, roleIdTokenHolder), "Onboarding should have been cancelled");
- }
+ vm.startPrank(userAddress);
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: e1, roleId: roleIdTokenHolder, signature: sigTokenHolder }));
- function _approveSelfOnboarding(address _userAddress, bytes32 entityId, bytes32 roleId) private {
vm.recordLogs();
- vm.startPrank(em.addr);
- nayms.approveSelfOnboarding(_userAddress, entityId, roleId);
- vm.stopPrank();
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: e1, roleId: roleIdCapitalProvider, signature: sigCapitalProvider }));
Vm.Log[] memory entries = vm.getRecordedLogs();
- assertEq(entries[0].topics.length, 2);
- assertEq(entries[0].topics[0], keccak256("SelfOnboardingApproved(address)"));
- assertEq(abi.decode(LibHelpers._bytes32ToBytes(entries[0].topics[1]), (address)), _userAddress);
- }
- function _selfOnboard(address _userAddress, bytes32 entityId, string memory groupName) private {
- vm.startPrank(_userAddress);
- nayms.onboard();
- vm.stopPrank();
-
- assertTrue(nayms.isInGroup(entityId, systemContext, groupName));
- assertTrue(nayms.isInGroup(entityId, entityId, groupName));
+ assertRoleUpdateEvent(entries, 0, systemContext, e1, roleIdTokenHolder, "_unassignRole");
+ assertRoleUpdateEvent(entries, 1, systemContext, e1, roleIdCapitalProvider, "_assignRole");
}
- function test_ApproveSelfOnboarding_InvalidEntityId() public {
+ function test_selfOnboarding_InvalidEntityId() public {
+ bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_CP);
nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
bytes32 entityId = keccak256("invalid entity id");
- bytes32 roleId = LibHelpers._stringToBytes32(LC.ROLE_ENTITY_TOKEN_HOLDER);
+ address userAddress = address(111);
- vm.startPrank(em.addr);
- vm.expectRevert(abi.encodeWithSelector(InvalidEntityId.selector, entityId));
- nayms.approveSelfOnboarding(address(111), entityId, roleId);
+ bytes memory sig = signWithPK(em.pk, nayms.getOnboardingHash(userAddress, entityId, roleId));
+
+ vm.startPrank(userAddress);
+ vm.expectRevert(abi.encodeWithSelector(InvalidObjectType.selector, entityId, LC.OBJECT_TYPE_ENTITY));
+ nayms.onboardViaSignature(OnboardingApproval({ entityId: entityId, roleId: roleId, signature: sig }));
+ vm.stopPrank();
}
function randomEntityId(uint256 salt) public view returns (bytes32) {
diff --git a/test/T04Market.t.sol b/test/T04Market.t.sol
index f4f69793..26471a01 100644
--- a/test/T04Market.t.sol
+++ b/test/T04Market.t.sol
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
-import { D03ProtocolDefaults, LibHelpers, LibObject, LC, c } from "./defaults/D03ProtocolDefaults.sol";
+import { D03ProtocolDefaults, LibHelpers, LC } from "./defaults/D03ProtocolDefaults.sol";
import { Vm } from "forge-std/Vm.sol";
import { StdStyle } from "forge-std/Test.sol";
import { MockAccounts } from "./utils/users/MockAccounts.sol";
-import { Entity, MarketInfo, FeeSchedule, SimplePolicy, Stakeholders, CalculatedFees } from "src/shared/FreeStructs.sol";
+import { Entity, MarketInfo, SimplePolicy, Stakeholders } from "src/shared/FreeStructs.sol";
import { StdStyle } from "forge-std/StdStyle.sol";
@@ -650,6 +650,86 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts {
// logOfferDetails(4); // should be filled 50%
}
+ function testOrderMatchedEventsForSecondaryTradeWithBetterThanAskPrice() public {
+ uint256 tokenAmount = 1000 ether;
+
+ writeTokenBalance(account0, naymsAddress, wethAddress, dt.entity1StartingBal);
+
+ changePrank(sm.addr);
+ nayms.assignRole(signer2Id, systemContext, LC.ROLE_ENTITY_CP);
+
+ // 1. Start token sale
+ nayms.createEntity(entity1, signer1Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test");
+ nayms.enableEntityTokenization(entity1, "E1PT", "E1-P-Token", 1e13);
+ nayms.startTokenSale(entity1, tokenAmount, tokenAmount); // SELL: PT 1000 / ETH 1000 (price = 1)
+
+ // 2. Purchase P-Tokens
+ nayms.createEntity(entity2, signer2Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test");
+ changePrank(signer2);
+ writeTokenBalance(signer2, naymsAddress, wethAddress, dt.entity2ExternalDepositAmt * 6);
+ nayms.externalDeposit(wethAddress, dt.entity2ExternalDepositAmt * 6);
+
+ // BUY: P 500 / E 500 (price = 1)
+ nayms.executeLimitOffer(wethId, tokenAmount / 2, entity1, tokenAmount / 2);
+
+ // SELL: P 500 / E 2000 (price = 4)
+ nayms.executeLimitOffer(entity1, tokenAmount / 2, wethId, tokenAmount * 2);
+
+ // 3. BUY P 1000
+ changePrank(sm.addr);
+ nayms.createEntity(entity3, signer3Id, initEntity(wethId, collateralRatio_500, maxCapital_2000eth, true), "test");
+
+ changePrank(signer3);
+ writeTokenBalance(signer3, naymsAddress, wethAddress, dt.entity2ExternalDepositAmt * 6);
+ nayms.externalDeposit(wethAddress, dt.entity2ExternalDepositAmt * 6);
+
+ vm.recordLogs();
+ nayms.executeLimitOffer(wethId, tokenAmount * 4, entity1, tokenAmount);
+
+ assertOfferFilled(1, entity1, entity1, tokenAmount, wethId, tokenAmount);
+ assertOfferFilled(2, entity2, wethId, tokenAmount / 2, entity1, tokenAmount / 2);
+ assertOfferFilled(3, entity2, entity1, tokenAmount / 2, wethId, tokenAmount * 2);
+ assertOfferFilled(4, entity3, wethId, tokenAmount * 4, entity1, tokenAmount);
+
+ // assert OrderMatched events ONLY for the last trade to verify match at a better-than-asked-for price
+ Vm.Log[] memory entries = vm.getRecordedLogs();
+
+ assertOrderMatchedEvent(entries, 11, 4, 1, tokenAmount / 2, tokenAmount / 2);
+ assertOrderMatchedEvent(entries, 12, 1, 4, tokenAmount / 2, tokenAmount / 2);
+ assertOrderMatchedEvent(entries, 21, 4, 3, tokenAmount * 2, tokenAmount / 2);
+ assertOrderMatchedEvent(entries, 22, 3, 4, tokenAmount / 2, tokenAmount * 2);
+
+ // logOfferDetails(1); // should be filled 100%
+ // logOfferDetails(2); // should be filled 100%
+ // logOfferDetails(3); // should be filled 100%
+ // logOfferDetails(4); // should be filled 100%
+ }
+
+ function assertOrderMatchedEvent(
+ Vm.Log[] memory _entries,
+ uint256 _entryIndex,
+ uint256 _orderId,
+ uint256 _matchedWithId,
+ uint256 _sellAmountMatched,
+ uint256 _buyAmountMatched
+ ) private {
+ assertEq(_entries[_entryIndex].topics.length, 2, string.concat("OrderMatched[", vm.toString(_orderId), "]: topics length incorrect"));
+ assertEq(
+ _entries[_entryIndex].topics[0],
+ keccak256("OrderMatched(uint256,uint256,uint256,uint256)"),
+ string.concat("OrderMatched[", vm.toString(_orderId), "]: Invalid event signature")
+ );
+ assertEq(
+ abi.decode(LibHelpers._bytes32ToBytes(_entries[_entryIndex].topics[1]), (uint256)),
+ _orderId,
+ string.concat("OrderMatched[", vm.toString(_orderId), "]: incorrect orderID")
+ ); // assert order ID
+ (uint256 matchedWithId, uint256 sellAmountMatched, uint256 buyAmountMatched) = abi.decode(_entries[_entryIndex].data, (uint256, uint256, uint256));
+ assertEq(matchedWithId, _matchedWithId, string.concat("OrderMatched[", vm.toString(_orderId), "]: invalid matchedWithID"));
+ assertEq(sellAmountMatched, _sellAmountMatched, string.concat("OrderMatched[", vm.toString(_orderId), "]: invalid sell amount"));
+ assertEq(buyAmountMatched, _buyAmountMatched, string.concat("OrderMatched[", vm.toString(_orderId), "]: invalid buy amount"));
+ }
+
function testBestOffersWithCancel() public {
testStartTokenSale();
@@ -932,7 +1012,6 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts {
uint256 prev1 = nayms.getOffer(bestId).rankPrev;
uint256 prev2 = nayms.getOffer(prev1).rankPrev;
- // c.log(" --------- ".red());
logOfferDetails(bestId);
logOfferDetails(prev1);
logOfferDetails(prev2);
@@ -986,7 +1065,7 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts {
nayms.enableEntityTokenization(userA.entityId, "E1", "Entity 1 Token", 1e6);
nayms.startTokenSale(userA.entityId, pToken100, usdc1000 * 2);
- /// Attack script
+ /// Attack script:
/// place order and lock funds
vm.startPrank(attacker.addr);
nayms.executeLimitOffer(usdcId, usdc1000, userA.entityId, pToken100);
@@ -1008,29 +1087,33 @@ contract T04MarketTest is D03ProtocolDefaults, MockAccounts {
vm.startPrank(sm.addr);
nayms.setMinimumSell(usdcId, 1e6);
assertEq(nayms.objectMinimumSell(usdcId), 1e6, "unexpected minimum sell amount");
+
bytes32 e1Id = createTestEntity(ea.id);
ea.entityId = e1Id;
nayms.enableEntityTokenization(e1Id, "E1", "Entity 1", 1e12);
hSetEntity(tcp, e1Id);
+
// Selling 10 pTokens for 1_000_000 USDC
nayms.startTokenSale(e1Id, 10e18, 1_000_000e6);
-
hAssignRole(tcp.id, e1Id, LC.ROLE_ENTITY_CP);
-
fundEntityUsdc(ea, 1_000_000e6);
- // If the amount being sold is less than the minimum sell amount, the offer is expected to go into the
- // "fulfilled" state
+
+ // If the amount being sold is less than the minimum sell amount, the offer is expected to go into the "fulfilled" state
vm.startPrank(tcp.addr);
+
(uint256 lastOfferId, , ) = nayms.executeLimitOffer(usdcId, 1e6 - 1, e1Id, 10e18);
MarketInfo memory m = logOfferDetails(lastOfferId);
assertEq(m.state, LC.OFFER_STATE_FULFILLED, "unexpected offer state");
+
(lastOfferId, , ) = nayms.executeLimitOffer(usdcId, 1e6, e1Id, 1e12 + 1);
m = logOfferDetails(lastOfferId);
assertEq(m.state, LC.OFFER_STATE_ACTIVE, "unexpected offer state");
+
(lastOfferId, , ) = nayms.executeLimitOffer(usdcId, 1e6 + 1, e1Id, 1e12);
m = logOfferDetails(lastOfferId);
assertEq(m.state, LC.OFFER_STATE_ACTIVE, "unexpected offer state");
+
(lastOfferId, , ) = nayms.executeLimitOffer(usdcId, 1e6, e1Id, 1e12 - 1);
m = logOfferDetails(lastOfferId);
assertEq(m.state, LC.OFFER_STATE_FULFILLED, "unexpected offer state");
diff --git a/test/T05Fees.t.sol b/test/T05Fees.t.sol
index 277feaac..80ecad9d 100644
--- a/test/T05Fees.t.sol
+++ b/test/T05Fees.t.sol
@@ -7,7 +7,7 @@ import { StdStyle } from "forge-std/Test.sol";
import { D03ProtocolDefaults, LC } from "./defaults/D03ProtocolDefaults.sol";
import { Entity, FeeSchedule, CalculatedFees } from "../src/shared/AppStorage.sol";
-import { SimplePolicy, SimplePolicyInfo, Stakeholders } from "../src/shared/FreeStructs.sol";
+import { SimplePolicy, Stakeholders } from "../src/shared/FreeStructs.sol";
import "src/shared/CustomErrors.sol";
import { LibHelpers } from "src/libs/LibHelpers.sol";
diff --git a/test/T05TokenWrapper.t.sol b/test/T05TokenWrapper.t.sol
index 1ae338ea..ea490f1d 100644
--- a/test/T05TokenWrapper.t.sol
+++ b/test/T05TokenWrapper.t.sol
@@ -4,7 +4,6 @@ pragma solidity 0.8.20;
import { Vm } from "forge-std/Vm.sol";
import { D03ProtocolDefaults, c, LC } from "./defaults/D03ProtocolDefaults.sol";
-import { Entity } from "src/shared/FreeStructs.sol";
import { ERC20Wrapper } from "../src/utils/ERC20Wrapper.sol";
contract T05TokenWrapper is D03ProtocolDefaults {
diff --git a/test/T06Staking.t.sol b/test/T06Staking.t.sol
index 152754e2..049b8a3d 100644
--- a/test/T06Staking.t.sol
+++ b/test/T06Staking.t.sol
@@ -1,15 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
-import { StdStorage, stdStorage, StdStyle, StdAssertions } from "forge-std/Test.sol";
-import { Vm } from "forge-std/Vm.sol";
+import { StdStorage, stdStorage, StdStyle } from "forge-std/Test.sol";
import { D03ProtocolDefaults, c, LC, LibHelpers } from "./defaults/D03ProtocolDefaults.sol";
-import { StakingConfig, StakingState } from "src/shared/FreeStructs.sol";
+import { StakingConfig } from "src/shared/FreeStructs.sol";
import { IDiamondCut } from "lib/diamond-2-hardhat/contracts/interfaces/IDiamondCut.sol";
import { StakingFixture } from "test/fixtures/StakingFixture.sol";
import { DummyToken } from "./utils/DummyToken.sol";
import { LibTokenizedVaultStaking } from "src/libs/LibTokenizedVaultStaking.sol";
-import { IERC20 } from "src/interfaces/IERC20.sol";
import { IntervalRewardPayedOutAlready, InvalidTokenRewardAmount, InvalidStakingAmount, InvalidStaker, EntityDoesNotExist, StakingAlreadyStarted, StakingNotStarted, StakingConfigDoesNotExist } from "src/shared/CustomErrors.sol";
@@ -27,17 +25,18 @@ contract T06Staking is D03ProtocolDefaults {
uint64 private constant R = (85 * SCALE_FACTOR) / 100;
uint64 private constant I = 30 days;
- bytes32 immutable VTOKENID = makeId2(LC.OBJECT_TYPE_ENTITY, bytes20(keccak256(bytes("test"))));
+ bytes32 internal immutable VTOKENID = makeId2(LC.OBJECT_TYPE_ENTITY, bytes20(keccak256(bytes("test"))));
- bytes32 VTOKENID1;
- bytes32 NAYM_ID;
+ bytes32 internal VTOKENID1;
+ bytes32 internal NAYM_ID;
- NaymsAccount bob;
- NaymsAccount sue;
- NaymsAccount lou;
- NaymsAccount nlf;
- DummyToken naymToken;
+ NaymsAccount internal bob;
+ NaymsAccount internal sue;
+ NaymsAccount internal lou;
+
+ NaymsAccount internal nlf;
+ DummyToken internal naymToken;
uint256 private constant usdcTotal = 1_000_000e6;
uint256 private constant wethTotal = 1_000_000e18;
@@ -48,18 +47,18 @@ contract T06Staking is D03ProtocolDefaults {
uint256 private constant totalStakeAmount = bobStakeAmount + sueStakeAmount + louStakeAmount;
- uint256 immutable rewardAmount = 100e6;
+ uint256 internal immutable rewardAmount = 100e6;
- uint256 constant stakingStart = 100 days;
+ uint256 internal constant stakingStart = 100 days;
StakingFixture internal stakingFixture;
- mapping(bytes32 entityId => uint256) usdcBalance;
- mapping(bytes32 entityId => mapping(uint64 index => uint256)) unclaimedReward;
+ mapping(bytes32 entityId => uint256) internal usdcBalance;
+ mapping(bytes32 entityId => mapping(uint64 index => uint256)) internal unclaimedReward;
- uint256 bobCurrentReward;
- uint256 sueCurrentReward;
- uint256 louCurrentReward;
+ uint256 internal bobCurrentReward;
+ uint256 internal sueCurrentReward;
+ uint256 internal louCurrentReward;
function setUp() public {
stakingFixture = new StakingFixture();
@@ -759,6 +758,48 @@ contract T06Staking is D03ProtocolDefaults {
assertEq(nayms.internalBalanceOf(bob.entityId, usdcId), rewardAmount * 2);
}
+ function test_fuzzCompoundReward(uint256 testReward) public {
+ vm.assume(100 < testReward && testReward < type(uint128).max);
+
+ naymToken.mint(nlf.addr, testReward);
+ vm.startPrank(nlf.addr);
+ naymToken.approve(address(nayms), testReward);
+ nayms.externalDeposit(address(naymToken), testReward);
+
+ uint256 startStaking = block.timestamp + 1;
+ initStaking(startStaking);
+
+ vm.warp(startStaking + 31 days);
+
+ startPrank(bob);
+ nayms.stake(nlf.entityId, bobStakeAmount);
+ assertStakedAmount(bob.entityId, bobStakeAmount, "Bob's stake should increase");
+
+ vm.warp(startStaking + 61 days);
+
+ assertEq(nayms.lastPaidInterval(nlf.entityId), 0, "Last interval paid should be 0");
+
+ startPrank(nlf);
+ nayms.payReward(makeId(LC.OBJECT_TYPE_STAKING_REWARD, bytes20("reward1")), nlf.entityId, usdcId, 10_000);
+ assertEq(nayms.lastPaidInterval(nlf.entityId), 2, "Last interval paid should be 0");
+
+ vm.warp(startStaking + 91 days);
+
+ startPrank(bob);
+ vm.expectRevert("No reward to compound");
+ nayms.compoundRewards(nlf.entityId);
+
+ startPrank(nlf);
+ nayms.payReward(makeId(LC.OBJECT_TYPE_STAKING_REWARD, bytes20("reward2")), nlf.entityId, NAYM_ID, testReward);
+ assertEq(nayms.lastPaidInterval(nlf.entityId), 3, "Last interval paid should be 0");
+
+ vm.warp(startStaking + 181 days);
+
+ startPrank(bob);
+ nayms.compoundRewards(nlf.entityId);
+ assertStakedAmount(bob.entityId, bobStakeAmount + testReward, "Bob's stake should increase");
+ }
+
function test_twoStakingRewardCurrencies() public {
uint256 startStaking = block.timestamp + 100 days;
initStaking(startStaking);
diff --git a/test/T07Zaps.t.sol b/test/T07Zaps.t.sol
new file mode 100644
index 00000000..fd2883a4
--- /dev/null
+++ b/test/T07Zaps.t.sol
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.20;
+
+import { D03ProtocolDefaults, LC, LibHelpers, StdStyle } from "./defaults/D03ProtocolDefaults.sol";
+import { DummyToken } from "test/utils/DummyToken.sol";
+import { StakingConfig, PermitSignature, OnboardingApproval } from "src/shared/FreeStructs.sol";
+import { InvalidERC20Token } from "src/shared/CustomErrors.sol";
+
+import { InvalidSignatureLength } from "src/shared/CustomErrors.sol";
+
+contract ZapFacetTest is D03ProtocolDefaults {
+ using LibHelpers for address;
+ using StdStyle for *;
+
+ DummyToken internal naymToken = new DummyToken();
+ DummyToken internal rewardToken;
+
+ NaymsAccount internal bob = makeNaymsAcc("Bob");
+ NaymsAccount internal sue = makeNaymsAcc("Sue");
+
+ NaymsAccount internal nlf = makeNaymsAcc(LC.NLF_IDENTIFIER);
+
+ uint64 private constant SCALE_FACTOR = 1_000_000; // 6 digits because USDC
+ uint64 private constant A = (15 * SCALE_FACTOR) / 100;
+ uint64 private constant R = (85 * SCALE_FACTOR) / 100;
+ uint64 private constant I = 30 days;
+ bytes32 internal NAYM_ID = address(naymToken)._getIdForAddress();
+ function initStaking(uint256 initDate) internal {
+ StakingConfig memory config = StakingConfig({
+ tokenId: NAYM_ID,
+ initDate: initDate,
+ a: A, // Amplification factor
+ r: R, // Boost decay factor
+ divider: SCALE_FACTOR,
+ interval: I // Amount of time per interval in seconds
+ });
+
+ startPrank(sa);
+ nayms.initStaking(nlf.entityId, config);
+ vm.stopPrank();
+ }
+
+ uint256 internal stakeAmount = 1e18;
+ uint256 internal unstakeAmount = 1e18;
+
+ function setUp() public {
+ naymToken.mint(bob.addr, stakeAmount);
+
+ startPrank(sa);
+ nayms.addSupportedExternalToken(address(naymToken), 100);
+
+ vm.startPrank(sm.addr);
+ hCreateEntity(bob.entityId, bob, entity, "Bob data");
+ hCreateEntity(nlf.entityId, nlf, entity, "NLF");
+ }
+
+ function test_zapStake_Success() public {
+ initStaking(block.timestamp + 1 + 7 days);
+
+ // Prepare permit data
+ uint256 deadline = block.timestamp;
+
+ // Create permit digest
+ bytes32 digest = keccak256(
+ abi.encodePacked(
+ "\x19\x01",
+ naymToken.DOMAIN_SEPARATOR(),
+ keccak256(abi.encode(naymToken.PERMIT_TYPEHASH(), bob.addr, address(nayms), stakeAmount, naymToken.nonces(owner), deadline))
+ )
+ );
+
+ // Sign the digest
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(bob.pk, digest);
+
+ startPrank(bob);
+
+ PermitSignature memory permitSignature = PermitSignature({ deadline: deadline, v: v, r: r, s: s });
+ OnboardingApproval memory approval;
+
+ vm.expectRevert(abi.encodeWithSelector(InvalidERC20Token.selector, address(111), "zapStake"));
+ nayms.zapStake(address(111), nlf.entityId, stakeAmount, stakeAmount, permitSignature, approval);
+
+ nayms.zapStake(address(naymToken), nlf.entityId, stakeAmount, stakeAmount, permitSignature, approval);
+
+ (uint256 staked, ) = nayms.getStakingAmounts(bob.entityId, nlf.entityId);
+
+ assertEq(stakeAmount, staked, "bob's stake amount should increase");
+ }
+
+ function test_zapOrder_Success() public {
+ changePrank(sm.addr);
+ nayms.assignRole(em.id, systemContext, LC.ROLE_ONBOARDING_APPROVER);
+
+ nayms.enableEntityTokenization(bob.entityId, "e1token", "e1token", 1e6);
+
+ // Selling bob p tokens for weth
+ nayms.startTokenSale(bob.entityId, 1 ether, 1 ether);
+
+ deal(address(weth), sue.addr, 10 ether);
+
+ // Prepare permit data
+ uint256 deadline = block.timestamp;
+ bytes32 PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
+ uint256 nonce = weth.nonces(sue.addr);
+ bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, sue.addr, address(nayms), 10 ether, nonce, deadline));
+ bytes32 digest = keccak256(abi.encodePacked("\x19\x01", weth.DOMAIN_SEPARATOR(), structHash));
+
+ bytes32 cpRoleId = LibHelpers._stringToBytes32(LC.ROLE_CAPITAL_PROVIDER);
+ bytes memory sig = signWithPK(em.pk, nayms.getOnboardingHash(sue.addr, sue.entityId, cpRoleId));
+
+ startPrank(sue);
+
+ // Sign the digest
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(sue.pk, digest);
+ PermitSignature memory permitSignature = PermitSignature({ deadline: deadline, v: v, r: r, s: s });
+
+ // Sign onboarding approval
+ OnboardingApproval memory onboardingApproval = OnboardingApproval({ entityId: sue.entityId, roleId: cpRoleId, signature: sig });
+
+ // verify token address
+ vm.expectRevert(abi.encodeWithSelector(InvalidERC20Token.selector, address(111), "zapOrder"));
+ nayms.zapOrder(address(111), 10 ether, wethId, 1 ether, bob.entityId, 1 ether, permitSignature, onboardingApproval);
+
+ OnboardingApproval memory oa = OnboardingApproval ({
+ entityId: onboardingApproval.entityId,
+ roleId: onboardingApproval.roleId,
+ signature: abi.encode(uint16(10000), onboardingApproval.entityId, onboardingApproval.roleId)
+ });
+
+ vm.expectRevert(abi.encodePacked(InvalidSignatureLength.selector));
+ nayms.zapOrder(address(weth), 10 ether, wethId, 1 ether, bob.entityId, 1 ether, permitSignature, oa);
+
+ // Caller should ensure they deposit enough to cover order fees.
+ nayms.zapOrder(address(weth), 10 ether, wethId, 1 ether, bob.entityId, 1 ether, permitSignature, onboardingApproval);
+
+ assertEq(nayms.internalBalanceOf(sue.entityId, bob.entityId), 1 ether, "sue should've purchased 1e18 bob p tokens");
+ }
+}
diff --git a/test/defaults/D01Deployment.sol b/test/defaults/D01Deployment.sol
index 03f480ed..6fcf6923 100644
--- a/test/defaults/D01Deployment.sol
+++ b/test/defaults/D01Deployment.sol
@@ -53,6 +53,7 @@ abstract contract D01Deployment is D00GlobalDefaults, Test {
function makeNaymsAcc(string memory name) public returns (NaymsAccount memory) {
(address addr, uint256 privateKey) = makeAddrAndKey(name);
+ vm.label(addr, name);
return NaymsAccount({ id: LibHelpers._getIdForAddress(addr), entityId: makeId(LC.OBJECT_TYPE_ENTITY, bytes20(keccak256(bytes(name)))), pk: privateKey, addr: addr });
}
diff --git a/test/defaults/D03ProtocolDefaults.sol b/test/defaults/D03ProtocolDefaults.sol
index 3537dc62..df9ca31d 100644
--- a/test/defaults/D03ProtocolDefaults.sol
+++ b/test/defaults/D03ProtocolDefaults.sol
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
+import { Vm } from "forge-std/Vm.sol";
+
import { D02TestSetup, LibHelpers, c } from "./D02TestSetup.sol";
import { Entity, SimplePolicy, MarketInfo, Stakeholders, FeeSchedule } from "src/shared/FreeStructs.sol";
-import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import { LibAdmin } from "src/libs/LibAdmin.sol";
-import { LibObject } from "src/libs/LibObject.sol";
import { LibConstants as LC } from "src/libs/LibConstants.sol";
import { StdStyle } from "forge-std/StdStyle.sol";
@@ -15,6 +15,7 @@ import { IERC20 } from "src/interfaces/IERC20.sol";
// solhint-disable no-console
// solhint-disable state-visibility
+// solhint-disable max-states-count
/// @notice Default test setup part 03
/// Protocol / project level defaults
@@ -440,16 +441,16 @@ contract D03ProtocolDefaults is D02TestSetup {
bytes[] memory signatures = new bytes[](4);
bytes32 signingHash = nayms.getSigningHash(policy.startDate, policy.maturationDate, policy.asset, policy.limit, offchainDataHash);
- signatures[0] = initPolicySig(0xACC2, signingHash);
- signatures[1] = initPolicySig(0xACC1, signingHash);
- signatures[2] = initPolicySig(0xACC3, signingHash);
- signatures[3] = initPolicySig(0xACC4, signingHash);
+ signatures[0] = signWithPK(0xACC2, signingHash);
+ signatures[1] = signWithPK(0xACC1, signingHash);
+ signatures[2] = signWithPK(0xACC3, signingHash);
+ signatures[3] = signWithPK(0xACC4, signingHash);
policyStakeholders = Stakeholders(roles, entityIds, signatures);
}
}
- function initPolicySig(uint256 privateKey, bytes32 signingHash) internal pure returns (bytes memory sig_) {
+ function signWithPK(uint256 privateKey, bytes32 signingHash) internal pure returns (bytes memory sig_) {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, MessageHashUtils.toEthSignedMessageHash(signingHash));
sig_ = abi.encodePacked(r, s, v);
}
@@ -481,4 +482,15 @@ contract D03ProtocolDefaults is D02TestSetup {
c.logBytes32(cr[i]);
}
}
+
+ function assertRoleUpdateEvent(Vm.Log[] memory entries, uint256 _index, bytes32 _ctxId, bytes32 _entityId, bytes32 _roleId, string memory _action) internal {
+ assertEq(entries[_index].topics.length, 2);
+ assertEq(entries[_index].topics[0], keccak256("RoleUpdated(bytes32,bytes32,bytes32,string)"));
+ assertEq(entries[_index].topics[1], _entityId, "incorrect entityID".red());
+
+ (bytes32 contextId, bytes32 roleId, string memory action) = abi.decode(entries[_index].data, (bytes32, bytes32, string));
+ assertEq(contextId, _ctxId, "incorrect context".red());
+ assertEq(_roleId, roleId, "incorrect role ID".red());
+ assertEq(action, _action, "incorrect action".red());
+ }
}
diff --git a/test/fixtures/LibFeeRouterFixture.sol b/test/fixtures/LibFeeRouterFixture.sol
index b04e63bf..ebd2e8c1 100644
--- a/test/fixtures/LibFeeRouterFixture.sol
+++ b/test/fixtures/LibFeeRouterFixture.sol
@@ -2,7 +2,7 @@
pragma solidity 0.8.20;
import { LibConstants } from "src/libs/LibConstants.sol";
-import { LibFeeRouter, CalculatedFees, FeeAllocation, FeeSchedule } from "src/libs/LibFeeRouter.sol";
+import { LibFeeRouter, CalculatedFees } from "src/libs/LibFeeRouter.sol";
/// Create a fixture to test the library LibFeeRouter
diff --git a/test/fixtures/TokenizedVaultFixture.sol b/test/fixtures/TokenizedVaultFixture.sol
index 51563f7f..936630e8 100644
--- a/test/fixtures/TokenizedVaultFixture.sol
+++ b/test/fixtures/TokenizedVaultFixture.sol
@@ -3,11 +3,13 @@ pragma solidity 0.8.20;
import { LibTokenizedVaultIO } from "src/libs/LibTokenizedVaultIO.sol";
import { LibAdmin } from "src/libs/LibAdmin.sol";
+import { InvalidERC20Token } from "src/shared/CustomErrors.sol";
contract TokenizedVaultFixture {
function externalDepositDirect(bytes32 _to, address _externalTokenAddress, uint256 _amount) public {
- // a user can only deposit an approved external ERC20 token
- require(LibAdmin._isSupportedExternalTokenAddress(_externalTokenAddress), "extDeposit: invalid ERC20 token");
+ if (!LibAdmin._isSupportedExternalTokenAddress(_externalTokenAddress)) {
+ revert InvalidERC20Token(_externalTokenAddress, "extDeposit");
+ }
LibTokenizedVaultIO._externalDeposit(_to, _externalTokenAddress, _amount);
}
}
diff --git a/test/utils/DummyToken.sol b/test/utils/DummyToken.sol
index 921d18e0..d1e0de1f 100644
--- a/test/utils/DummyToken.sol
+++ b/test/utils/DummyToken.sol
@@ -2,8 +2,11 @@
pragma solidity 0.8.20;
import { IERC20 } from "src/interfaces/IERC20.sol";
+import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract DummyToken is IERC20 {
+ using ECDSA for bytes32;
+
string public name = "Dummy";
string public symbol = "DUM";
uint8 public decimals = 18;
@@ -11,38 +14,80 @@ contract DummyToken is IERC20 {
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
- function transfer(address to, uint256 value) external returns (bool) {
- if (value == 0) {
- return false;
+ // EIP-2612 permit state variables
+ bytes32 public DOMAIN_SEPARATOR;
+ // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
+ bytes32 public constant PERMIT_TYPEHASH = 0x6d47c92dbe9aa29a8e9e38d25f3f54ab645e5df690ddf0d3e2a24ec2445a44f0;
+ mapping(address => uint256) public nonces;
+
+ constructor() {
+ uint256 chainId;
+ assembly {
+ chainId := chainid()
}
+ DOMAIN_SEPARATOR = keccak256(
+ abi.encode(
+ // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
+ 0x8b73e7bb5ba7313e92d4a46294e43b9c1bafabf1adbe7b6f4bdfd44c38a7e6d4,
+ keccak256(bytes(name)),
+ keccak256(bytes("1")),
+ chainId,
+ address(this)
+ )
+ );
+ }
+ function transfer(address to, uint256 value) external returns (bool) {
require(balanceOf[msg.sender] >= value, "not enough balance");
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
+ emit Transfer(msg.sender, to, value);
return true;
}
function approve(address spender, uint256 value) external returns (bool) {
allowance[msg.sender][spender] = value;
+ emit Approval(msg.sender, spender, value);
return true;
}
function transferFrom(address from, address to, uint256 value) external returns (bool) {
- if (value == 0) {
- revert();
- }
-
require(allowance[from][msg.sender] >= value, "not enough allowance");
require(balanceOf[from] >= value, "not enough balance");
+ allowance[from][msg.sender] -= value;
balanceOf[from] -= value;
balanceOf[to] += value;
+ emit Transfer(from, to, value);
return true;
}
function mint(address to, uint256 value) external {
balanceOf[to] += value;
totalSupply += value;
+ emit Transfer(address(0), to, value);
}
- function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {}
+ /**
+ * @notice Approves tokens via signature, as per EIP-2612
+ * @param owner The token owner's address
+ * @param spender The spender's address
+ * @param value The amount to approve
+ * @param deadline The deadline timestamp by which the permit must be used
+ * @param v The recovery byte of the signature
+ * @param r Half of the ECDSA signature pair
+ * @param s Half of the ECDSA signature pair
+ */
+ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override {
+ require(block.timestamp <= deadline, "permit: expired deadline");
+
+ bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline));
+
+ bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash));
+
+ address recoveredAddress = digest.recover(v, r, s);
+ require(recoveredAddress == owner, "permit: invalid signature");
+
+ allowance[owner][spender] = value;
+ emit Approval(owner, spender, value);
+ }
}